# Tests for Phase 5b: Full pipeline integration (get_problem_data)
# Reference data generated by CVXPY (scripts/cvxpy_reference.py)

library(CVXR)

# ===================================================================
# get_problem_data parity with CVXPY
# ===================================================================

## @cvxpy NONE
test_that("get_problem_data: LP min sum(x) s.t. x >= 1", {
  x <- Variable(3)
  p <- Problem(Minimize(sum_entries(x)), list(x >= 1))
  result <- problem_data(p, CLARABEL_SOLVER)

  data <- result$data
  expect_equal(data$c, c(1, 1, 1))
  expect_equal(as.matrix(data$A), -diag(3))
  expect_equal(as.numeric(data$b), c(-1, -1, -1))
  expect_equal(data$dims@zero, 0L)
  expect_equal(data$dims@nonneg, 3L)

  ## Chain and inverse_data exist
  expect_true(!is.null(result$chain))
  expect_true(!is.null(result$inverse_data))
})

## @cvxpy NONE
test_that("get_problem_data: LP min 2*x s.t. x>=0, x<=5", {
  x <- Variable(1)
  p <- Problem(Minimize(2 * x), list(x >= 0, x <= 5))
  data <- problem_data(p, CLARABEL_SOLVER)$data

  expect_equal(data$c, 2.0)
  expect_equal(as.numeric(data$b), c(0, 5))
  A <- as.matrix(data$A)
  expect_equal(A[1, 1], -1)
  expect_equal(A[2, 1], 1)
  expect_equal(data$dims@nonneg, 2L)
})

## @cvxpy NONE
test_that("get_problem_data: LP with equality constraint", {
  xy <- Variable(2)
  p <- Problem(Minimize(sum_entries(xy)), list(xy[1] + xy[2] == 5, xy >= 0))
  data <- problem_data(p, CLARABEL_SOLVER)$data

  ## CVXPY: c=[1,1], A=[[1,1],[-1,0],[0,-1]], b=[5,0,0], dims={zero:1,nonneg:2}
  expect_equal(data$c, c(1, 1))
  expect_equal(as.numeric(data$b), c(5, 0, 0))
  A <- as.matrix(data$A)
  expect_equal(A[1, ], c(1, 1))
  expect_equal(A[2, 1], -1)
  expect_equal(A[3, 2], -1)
  expect_equal(data$dims@zero, 1L)
  expect_equal(data$dims@nonneg, 2L)
})

## @cvxpy NONE
test_that("get_problem_data: SOCP", {
  xv <- Variable(1)
  yz <- Variable(2)
  p <- Problem(Minimize(xv), list(SOC(xv, yz)))
  data <- problem_data(p, CLARABEL_SOLVER)$data

  ## CVXPY: c=[1,0,0], A=-I_3, b=[0,0,0], dims={soc:[3]}
  expect_equal(length(data$c), 3L)
  expect_equal(data$c[1], 1)
  expect_equal(as.matrix(data$A), -diag(3))
  expect_equal(as.numeric(data$b), c(0, 0, 0))
  expect_equal(data$dims@soc, 3L)
  expect_equal(data$dims@zero, 0L)
  expect_equal(data$dims@nonneg, 0L)
})

## @cvxpy NONE
test_that("get_problem_data: nonneg variable attribute", {
  x <- Variable(2, nonneg = TRUE)
  p <- Problem(Minimize(sum_entries(x)), list(x <= 3))
  data <- problem_data(p, CLARABEL_SOLVER)$data

  ## CVXPY: c=[1,1], A=[[-1,0],[0,-1],[1,0],[0,1]], b=[0,0,3,3], dims={nonneg:4}
  expect_equal(data$c, c(1, 1))
  expect_equal(as.numeric(data$b), c(0, 0, 3, 3))
  expect_equal(data$dims@nonneg, 4L)
  expect_equal(data$dims@zero, 0L)
  A <- as.matrix(data$A)
  expect_equal(nrow(A), 4L)
  expect_equal(ncol(A), 2L)
})

## @cvxpy NONE
test_that("get_problem_data: Maximize is flipped", {
  x <- Variable(2)
  p <- Problem(Maximize(sum_entries(x)), list(x <= 1))
  data <- problem_data(p, CLARABEL_SOLVER)$data

  ## Maximizing sum(x) → Minimize -sum(x), so c = [-1, -1]
  expect_equal(data$c, c(-1, -1))
})

## @cvxpy NONE
test_that("get_problem_data: constant offset in inverse_data", {
  x <- Variable(1)
  p <- Problem(Minimize(x + 10))
  result <- problem_data(p, CLARABEL_SOLVER)

  ## Offset is not in solver data — it's tracked in inverse_data
  ## for recovery during solution inversion
  expect_equal(result$data$c, 1)

  ## The inverse_data list contains per-reduction inverse data
  ## The ConicSolver stores offset in its inv_data
  inv_list <- result$inverse_data
  ## Last element in inv_list is ConicSolver's inv_data
  solver_inv <- inv_list[[length(inv_list)]]
  expect_equal(as.numeric(solver_inv$offset), 10)
})

# ===================================================================
# Chain roundtrip
# ===================================================================

## @cvxpy NONE
test_that("Chain apply+invert roundtrip with FlipObjective", {
  x <- Variable(2)
  p <- Problem(Maximize(sum_entries(x)), list(x <= 1))
  chain <- Chain(reductions = list(FlipObjective()))

  result <- reduction_apply(chain, p)
  inv_data <- result[[2]]

  ## Mock solution for the negated problem
  sol <- Solution(status = "optimal", opt_val = -5.0,
                  primal_vars = list(), dual_vars = list())
  recovered <- reduction_invert(chain, sol, inv_data)
  expect_equal(recovered@opt_val, 5.0)
})

# ===================================================================
# Dimension checks
# ===================================================================

## @cvxpy NONE
test_that("A dimensions match: rows = total constraint size, cols = num vars", {
  x <- Variable(3)
  y <- Variable(2)
  p <- Problem(Minimize(sum_entries(x) + sum_entries(y)),
               list(x >= 0, y >= 0, x <= 5, y <= 3))
  data <- problem_data(p, CLARABEL_SOLVER)$data

  ## 4 constraints of sizes 3, 2, 3, 2 → total rows = 10
  ## Variables: 3 + 2 = 5 columns
  expect_equal(nrow(data$A), 10L)
  expect_equal(ncol(data$A), 5L)
  expect_equal(length(data$c), 5L)
  expect_equal(length(data$b), 10L)
  expect_equal(data$dims@nonneg, 10L)
})

## @cvxpy NONE
test_that("get_problem_data rejects non-DCP", {
  x <- Variable(1)
  p <- Problem(Maximize(exp(x)))  # exp is convex, maximize convex is non-DCP
  expect_error(problem_data(p, CLARABEL_SOLVER), "not DCP")
})
