# Generated by CodiumAI
from gMCSpy.Utilities import setSolver
import gMCSpy
import scipy
import numpy as np
import cplex
import gurobipy
try: 
    import pyscipopt
except ImportError:
    pass
import random

# Dependencies:
# pip install pytest-mock
import pytest

class TestSetsolver:

    # Tests that the function returns the correct interface and boolean value when the default solver is 'cplex' and useIndicators is True
    def test_default_solver_cplex_use_indicators_true(self, mocker):
        # Mock the import of cplex module
        mocker.patch('importlib.import_module', return_value=mocker.Mock())
    
        # Call the setSolver function with defaultSolver='cplex' and useIndicators=True
        result = setSolver('cplex', True)
    
        # Assert that the returned boolean value is True
        assert result[1] == True
    
        # Assert that the interface is cplex
        assert result[0] == cplex

    # Tests that the function returns the correct interface and boolean value when the default solver is 'gurobi' and useIndicators is True
    def test_default_solver_gurobi_use_indicators_true(self, mocker):
        # Mock the import of gurobipy module
        mocker.patch('importlib.import_module', return_value=mocker.Mock())
    
        # Call the setSolver function with defaultSolver='gurobi' and useIndicators=True
        result = setSolver('gurobi', True)

        # Assert that the returned boolean value is True
        assert result[1] == True
    
        # Assert that the interface is gurobi
        assert result[0] == gurobipy  

    # Tests that the function returns the correct interface and boolean value when the default solver is 'scip' and useIndicators is False
    def test_default_solver_scip_use_indicators_true(self, mocker):
        # Mock the import of pyscipopt module
        mocker.patch('importlib.import_module', return_value=mocker.Mock())
    
        # Call the setSolver function with defaultSolver='scip' and useIndicators=False
        #result = setSolver('scip', True)

        # Assert that the interface is pyscipopt
        #assert result[0] == pyscipopt    

        # Assert that the returned boolean value is False
        #assert result[1] == True
    


    # Tests that the function prints an error message when the default solver is not supported
    def test_default_solver_not_supported(self, capsys):
        # Call the setSolver function with defaultSolver='unsupported'
        setSolver('unsupported')
    
        # Capture the printed output
        captured = capsys.readouterr()
    
        # Assert that the error message is printed
        assert captured.out == "The default solver is not supported, please select (cplex, gurobi, scip)\n"

class TestCalculations:
    # Define the list of solvers to test
    solvers = ["cplex", "gurobi"]
    
    # test that the function returns the correct solution for the toy model using different solvers
    @pytest.mark.parametrize("solver", solvers)
    def test_GeneralityOfSolvers(self, solver):
        problem = gMCSpy.OptimizationProblem()
        problem.addVariable(0, 0, 'infinity', 'continuous')
        problem.addVariable(1, 0, 'infinity', 'continuous')
        problem.addVariable(2, 0, 'infinity', 'continuous')

        problem.addConstraint('c0',variables=[0,1,2], coefficients=[1,1,1], sense='L', rhs=100)
        problem.addConstraint('c1',variables=[0,1,2], coefficients=[10,4,5], sense='L', rhs=600)
        problem.addConstraint('c2',variables=[0,1,2], coefficients=[2,2,6], sense='L', rhs=300)

        problem.addObjective(variables=[0,1,2], coefficients=[10,6,4], sense='maximize')

        if solver == "gurobi":
            problem.setSolver(solver)
            [problemInterpreted, interface]= problem.interpretProblem()
            problemInterpreted.optimize()
            results = [
                (v.varName, v.X) for v in problemInterpreted.getVars() if abs(v.obj) > 1e-6
            ]
            objValue = round(problemInterpreted.objVal, 2)
            assert (objValue == 733.33) and (
                results
                == [("x_0", 33.333333333333336), ("x_1", 66.66666666666666), ("x_2", 0.0)]
            )
        elif solver == "cplex":
            problem.setSolver(solver)
            [problemInterpreted, interface]= problem.interpretProblem()
            problemInterpreted.solve()
            objVal = problemInterpreted.solution.get_objective_value()
            vars = problemInterpreted.solution.get_values()
            vars = [round(v, 2) for v in vars]
            assert (round(objVal, 2) == 733.33) and (vars == [33.33, 66.67, 0.0])
        elif solver == "scip":
            problem.setSolver(solver)
            [problemInterpreted, interface]= problem.interpretProblem()
            problemInterpreted.optimize()
            objVal = problemInterpreted.getObjVal()
            vars = problemInterpreted.getVars()
            vars = [round(problemInterpreted.getVal(v), 2) for v in vars]
            assert (round(objVal, 2) == 733.33) and (vars == [33.33, 66.67, 0.0])

    # Define the parameters to test
    FORCE_LENGTH_VALUES = [True, False]

    @pytest.mark.parametrize("solver", solvers)
    @pytest.mark.parametrize("force_length", FORCE_LENGTH_VALUES)
    def test_calculate_mcs_with_solver_and_force_length(self, solver, force_length):
        toy_model = gMCSpy.Utilities.createToyModel()
        mcs = gMCSpy.calculateMCS(toy_model, 1e6, 7, solver=solver,  forceLength=force_length)
        # Add assertions here to test the results with the specified solver
        expectedMCS = [['r1'], ['r7'], ['r8'], ['r2', 'r3'], ['r2', 'r4', 'r6'],  
                    ['r2', 'r4', 'r5']]
        falsePositive = False
        allResults = 0
        for information in mcs.items():
            solution = information[1]['solution']
            if solution in expectedMCS:
                allResults += 1
            else:
                falsePositive = True
        assert falsePositive == False
        assert allResults == len(expectedMCS)
        
    # Parametrize the tests with different solvers
    @pytest.mark.parametrize("solver", solvers)
    def test_calculate_mcs_rxn_knockout_multiple_times(self, solver):
        toy_model = gMCSpy.Utilities.createToyModel()
        for _ in range(3):  # Run the test three times
            possibleKOs = ['r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7']
            random_element = random.choice(possibleKOs)
            mcs = gMCSpy.calculateMCS(toy_model, 1e6, 7, rxnKnockOut=[random_element], solver=solver)
            falsePositive = False
            for information in mcs.items():
                solution = information[1]['solution']
                if random_element not in solution:
                    falsePositive = True
            assert falsePositive == False
        
    # Parametrize the tests with different solvers
    @pytest.mark.parametrize("solver", solvers)
    def test_calculate_mcs_rxn_knockout_multiple(self, solver):
        toy_model = gMCSpy.Utilities.createToyModel()
        mcs = gMCSpy.calculateMCS(toy_model, 1e6, 7, solver=solver, rxnSubset=['r1','r2','r3','r4','r8'])
        expectedMCS = [['r1'], ['r8'], ['r2', 'r3']]
        falsePositive = False
        allResults = 0
        for information in mcs.items():
            solution = information[1]['solution']
            if solution in expectedMCS:
                allResults += 1
            else:
                falsePositive = True
        assert falsePositive == False
        assert allResults == len(expectedMCS)
        
    # Parametrize the tests with different solvers
    @pytest.mark.parametrize("solver", solvers)
    def test_calculate_gmcs_multiple(self, solver):
        toy_model = gMCSpy.Utilities.createToyModel()
        groundTruth = [['gene1'], ['gene2'], ['gene3'], ['gene6'], 
                       ['gene8'], ['gene9'], ['gene10']]
        solutions = gMCSpy.calculateGMCS.calculateGeneMCS(toy_model, solver=solver)
        count = 0
        for key, values in solutions.items():
            solution = values['solution']
            for gtSolution in groundTruth:
                if solution == frozenset(gtSolution):
                   count += 1
        assert count == len(groundTruth)