import pytest
import mipx

SOLVERS = ["CBC", "SCIP", "GUROBI", "CPLEX", "SAT", "CP", "CPLEX_CP"]
CP_SOLVERS = ["CP", "CPLEX_CP"]


@pytest.fixture(params=SOLVERS)
def model(request):
    if request.param in CP_SOLVERS:
        return mipx.CpModel(solver_id=request.param)
    return mipx.Model(solver_id=request.param)


@pytest.fixture(params=CP_SOLVERS)
def cpmodel(request):
    return mipx.CpModel(solver_id=request.param)


@pytest.mark.filterwarnings("ignore::DeprecationWarning")
@pytest.mark.usefixtures("model")
class TestModel:
    def test_add_constr(self, model: mipx.Model):
        x = model.addVar(ub=1)
        y = model.addVars(10)
        model.addConstr(x == 1)
        model.addConstr(y.quicksum() == 10)
        y.quicksum([1, 2])
        status = model.optimize()
        assert mipx.success(status)
        assert x.X == 1
        # assert model.valueExpression(y.quicksum()) == 10

    def test_gen_constr_and(self, model: mipx.Model):
        x = model.addVars(6, vtype=mipx.BINARY)
        y = model.addVar(vtype=mipx.BINARY)
        model.addConstr(x[0] == 0)
        model.addConstr(x.quicksum() == 5)
        model.addGenConstrAnd(y, x.quickselect())
        m = model.addVar(vtype=mipx.BINARY, ub=1)
        z = model.addVars(3, vtype=mipx.BINARY, ub=1)
        model.addConstr(z.quicksum() == 3)
        model.addGenConstrAnd(m, z.quickselect())
        status = model.optimize()
        assert mipx.success(status)
        assert y.X == 0
        assert m.X == 1

    def test_gen_constr_or(self, model: mipx.Model):
        x = model.addVars(6, vtype=mipx.BINARY)
        y = model.addVar(vtype=mipx.BINARY)
        model.addConstr(x[0] == 0)
        model.addConstr(x.quicksum() == 5)
        model.addGenConstrOr(y, x.quickselect())
        m = model.addVar(vtype=mipx.BINARY, ub=1)
        z = model.addVars(3, vtype=mipx.BINARY, ub=1)
        model.addConstr(z.quicksum() == 0)
        model.addGenConstrOr(m, z.quickselect())
        status = model.optimize()
        assert mipx.success(status)
        assert y.X == 1
        assert m.X == 0

    def test_gen_constr_xor(self, model: mipx.Model):
        x = model.addVars(2, vtype=mipx.BINARY)
        y = model.addVar(vtype=mipx.BINARY)
        # 不同时
        model.addConstrs([x[0] == 0, x[1] == 1])
        model.addGenConstrXOr(y, x.quickselect())
        a = model.addVars(2, vtype=mipx.BINARY)
        b = model.addVar(vtype=mipx.BINARY)
        # 同时
        model.addConstrs([a[0] == 1, a[1] == 1])
        model.addGenConstrXOr(b, a.quickselect())
        c = model.addVars(2, vtype=mipx.BINARY)
        d = model.addVar(vtype=mipx.BINARY)
        # 同时
        model.addConstrs([c[0] == 0, c[1] == 0])
        model.addGenConstrXOr(d, c.quickselect())
        e = model.addVars(2, vtype=mipx.BINARY)
        f = model.addVar(vtype=mipx.BINARY)
        # 同时
        model.addConstrs([e[0] == 1, e[1] == 0])
        model.addGenConstrXOr(f, e.quickselect())
        status = model.optimize()
        assert mipx.success(status)
        assert mipx.succ(status)
        assert y.X == 1
        assert b.X == 0
        assert d.X == 0
        assert f.X == 1

    def test_gen_constr_pwl(self, model: mipx.Model):
        x = model.addVar(ub=10, vtype=mipx.INTEGER)
        y = model.addVar(ub=100, vtype=mipx.INTEGER)
        z = model.addVar(lb=1, ub=10, vtype=mipx.INTEGER)
        model.addGenConstrPWL(x, y, [4, 10], [1, 5 + z, 10], M=1000)
        model.setObjective(y)
        status = model.optimize(mipx.MAXIMIZE)
        assert mipx.success(status)
        assert y.X == 15
        assert z.X == 10

    # def test_gen_constr_indicator(self, model: mipx.Model):
    #     x = model.addVar(ub=10, vtype=mipx.INTEGER)
    #     y = model.addVar(ub=100, vtype=mipx.INTEGER)
    #     z = model.addVar(lb=1, ub=10, vtype=mipx.INTEGER)
    #     model.addGenConstrPWL(x, y, [4, 10], [1, 5 + z, 10], M=1000)
    #     model.setObjective(y)
    #     status = model.optimize(mipx.MAXIMIZE)
    #     assert mipx.success(status)
    #     assert y.X == 15
    #     assert z.X == 10

    def test_gen_constr_abs(self, model: mipx.Model):
        x = model.addVar(lb=-10, ub=10, vtype=mipx.INTEGER)
        y = model.addVar(ub=10, vtype=mipx.INTEGER)
        model.addGenConstrAbs(y, x, M=1000)
        model.addConstr(x == -5)
        status = model.optimize(mipx.MAXIMIZE)
        assert mipx.success(status)
        assert y.X == 5
