# %%
PROJECT_PATH = '/home/md/Work/ligalytics/leagues_stable/'
import os, sys
sys.path.insert(0, PROJECT_PATH)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "leagues.settings")
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
import random
import time

# XPRESS ENVIRONMENT
os.environ['XPRESSDIR'] = "/opt/xpressmp_8.4"
os.environ['XPRESS'] = "/opt/xpressmp_8.4/bin"
os.environ['LD_LIBRARY_PATH'] = os.environ['XPRESSDIR']+"/lib:"+os.environ['LD_LIBRARY_PATH']
os.environ['DYLD_LIBRARY_PATH'] = os.environ['XPRESSDIR']+"/lib:"
os.environ['SHLIB_PATH'] = os.environ['XPRESSDIR']+"/lib:"
os.environ['LIBPATH'] = os.environ['XPRESSDIR']+"/lib:"
# os.environ['PYTHONPATH'] =os.environ['XPRESSDIR']+"/lib:"+os.environ['PYTHONPATH']
os.environ['PYTHONPATH'] =os.environ['XPRESSDIR']+"/lib:"
os.environ['CLASSPATH'] =os.environ['XPRESSDIR']+"/lib/xprs.jar:"
os.environ['CLASSPATH'] =os.environ['XPRESSDIR']+"/lib/xprb.jar:"+os.environ['CLASSPATH']
os.environ['CLASSPATH'] =os.environ['XPRESSDIR']+"/lib/xprm.jar:"+os.environ['CLASSPATH']
os.environ['PATH'] =os.environ['XPRESSDIR']+"/bin:"+os.environ['PATH']


from leagues import settings
settings.DATABASES['default']['NAME'] = PROJECT_PATH+'/db.sqlite3'

import django
django.setup()

from scheduler.models import *
from pulp import *
import csv

import xpress as xp
xp.controls.outputlog = 0



scenario = Scenario.objects.get(id=32)

# %%


teams = Team.objects.filter(season=scenario.season,active=True)

countries = teams.values_list('country', flat=True).distinct()
teams_from_country = {
    c:[t for t in teams if t.country==c] for c in countries 
}

pot = {}
for i in teams.values_list('pot',flat=True).distinct():
    pot[i] = teams.filter(pot=i)


# directed version
# requirement = {
#     1:[1,-1,2,-2,3,-3,4,-4],
#     2:[1,-1,2,-2,3,-3,4,-4],
#     3:[1,-1,2,-2,3,-3,4,-4],
#     4:[1,-1,2,-2,3,-3,4,-4],
# }
# undirected version
requirement = {
    1:{
        1:2,
        2:2,
        3:2,
        4:2,
    },
    2:{
        1:2,
        2:2,
        3:2,
        4:2,
    },
    3:{
        1:2,
        2:2,
        3:2,
        4:2,
    },
    4:{
        1:2,
        2:2,
        3:2,
        4:2,
    },
}

def check_feasible(fixed_games):
    model = xp.problem(name='Draws', sense=xp.minimize)
    x = {}
    for t1 in teams:
        for t2 in teams:
            if t1.country != t2.country:
                x[t1.id, t2.id] = xp.var(ub=1, vartype=xp.integer)

    model.addVariable(x)

    # REQUIREMENTS
    for t in teams:
        for r,val in requirement[t.pot].items():
            model.addConstraint(xp.Sum(x[t.id,t2.id] for t2 in pot[r] if (t.id,t2.id) in x.keys()) == val)
        for c in countries:
            if c != t.country:
                model.addConstraint(xp.Sum(x[t.id,t2.id] for t2 in teams_from_country[c]) <= 3)

    # FIXATIONS
    for (t1,t2) in fixed_games:
        # print("FIXING",t1,t2)
        model.addConstraint(x[t1,t2] == 1)


    for (t1,t2) in x.keys():
        model.addConstraint(x[t1,t2] == x[t2,t1])

    start_time = time()
    model.solve()
    comp_time = time()-start_time

    if model.getProbStatus() != 6:
        # print("INFEASIBLE FOUND")
        return False
    else:
        return True


# %%

for i in range(1,1000):
    print("SIMULATION",i)

    potA = list(teams.filter(pot=1).values_list('id', flat=True))
    potB = list(teams.filter(pot=2).values_list('id', flat=True))
    potC = list(teams.filter(pot=3).values_list('id', flat=True))
    potD = list(teams.filter(pot=4).values_list('id', flat=True))

    possible_Aopps = {
        t.id: [t2.id for t2 in teams.filter(pot=1) if t2.country != t.country] for t in teams
    }
    possible_Bopps = {
        t.id: [t2.id for t2 in teams.filter(pot=2) if t2.country != t.country] for t in teams
    }
    possible_Copps = {
        t.id: [t2.id for t2 in teams.filter(pot=3) if t2.country != t.country] for t in teams
    }
    possible_Dopps = {
        t.id: [t2.id for t2 in teams.filter(pot=4) if t2.country != t.country] for t in teams
    }

    Aopps = defaultdict(lambda:[])
    Bopps = defaultdict(lambda:[])
    Copps = defaultdict(lambda:[])
    Dopps = defaultdict(lambda:[])

    fixed_games = []

    infeasible = False

    while(potA):    
        new_team = potA.pop(random.randint(0,len(potA)-1))

        while(len(Aopps[new_team]) < 2 and len(possible_Aopps[new_team]) > 0):        
            new_opponent = random.choice(possible_Aopps[new_team])
            if len(Aopps[new_opponent]) < 2:            
                if check_feasible(fixed_games+[(new_team,new_opponent)]):                
                    Aopps[new_team].append(new_opponent)
                    Aopps[new_opponent].append(new_team)
                    fixed_games.append((new_team,new_opponent))
                possible_Aopps[new_team].remove(new_opponent)            
            else:
                possible_Aopps[new_team].remove(new_opponent)    

        if len(Aopps[new_team]) < 2:
            infeasible = True
            break

        while(len(Bopps[new_team]) < 2 and len(possible_Bopps[new_team]) > 0):        
            new_opponent = random.choice(possible_Bopps[new_team])
            if len(Aopps[new_opponent]) < 2:            
                if check_feasible(fixed_games+[(new_team,new_opponent)]):                
                    Bopps[new_team].append(new_opponent)
                    Aopps[new_opponent].append(new_team)
                    fixed_games.append((new_team,new_opponent))
                possible_Bopps[new_team].remove(new_opponent)            
            else:
                possible_Bopps[new_team].remove(new_opponent)    

        if len(Bopps[new_team]) < 2:
            infeasible = True
            break

        while(len(Copps[new_team]) < 2 and len(possible_Copps[new_team]) > 0):        
            new_opponent = random.choice(possible_Copps[new_team])
            if len(Aopps[new_opponent]) < 2:            
                if check_feasible(fixed_games+[(new_team,new_opponent)]):                
                    Copps[new_team].append(new_opponent)
                    Aopps[new_opponent].append(new_team)
                    fixed_games.append((new_team,new_opponent))
                possible_Copps[new_team].remove(new_opponent)            
            else:
                possible_Copps[new_team].remove(new_opponent)    

        if len(Copps[new_team]) < 2:
            infeasible = True
            break

        while(len(Dopps[new_team]) < 2 and len(possible_Dopps[new_team]) > 0):        
            new_opponent = random.choice(possible_Dopps[new_team])
            if len(Aopps[new_opponent]) < 2:            
                if check_feasible(fixed_games+[(new_team,new_opponent)]):                
                    Dopps[new_team].append(new_opponent)
                    Aopps[new_opponent].append(new_team)
                    fixed_games.append((new_team,new_opponent))
                possible_Dopps[new_team].remove(new_opponent)            
            else:
                possible_Dopps[new_team].remove(new_opponent)    

        if len(Dopps[new_team]) < 2:
            infeasible = True
            break


    while(potB):    
        new_team = potB.pop(random.randint(0,len(potB)-1))

        while(len(Bopps[new_team]) < 2 and len(possible_Bopps[new_team]) > 0):        
            new_opponent = random.choice(possible_Bopps[new_team])
            if len(Bopps[new_opponent]) < 2:            
                if check_feasible(fixed_games+[(new_team,new_opponent)]):                
                    Bopps[new_team].append(new_opponent)
                    Bopps[new_opponent].append(new_team)
                    fixed_games.append((new_team,new_opponent))
                possible_Bopps[new_team].remove(new_opponent)            
            else:
                possible_Bopps[new_team].remove(new_opponent)    

        if len(Bopps[new_team]) < 2:
            infeasible = True
            break

        while(len(Copps[new_team]) < 2 and len(possible_Copps[new_team]) > 0):        
            new_opponent = random.choice(possible_Copps[new_team])
            if len(Bopps[new_opponent]) < 2:            
                if check_feasible(fixed_games+[(new_team,new_opponent)]):                
                    Copps[new_team].append(new_opponent)
                    Bopps[new_opponent].append(new_team)
                    fixed_games.append((new_team,new_opponent))
                possible_Copps[new_team].remove(new_opponent)            
            else:
                possible_Copps[new_team].remove(new_opponent)    

        if len(Copps[new_team]) < 2:
            infeasible = True
            break

        while(len(Dopps[new_team]) < 2 and len(possible_Dopps[new_team]) > 0):        
            new_opponent = random.choice(possible_Dopps[new_team])
            if len(Bopps[new_opponent]) < 2:            
                if check_feasible(fixed_games+[(new_team,new_opponent)]):                
                    Dopps[new_team].append(new_opponent)
                    Bopps[new_opponent].append(new_team)
                    fixed_games.append((new_team,new_opponent))
                possible_Dopps[new_team].remove(new_opponent)            
            else:
                possible_Dopps[new_team].remove(new_opponent)    

        if len(Dopps[new_team]) < 2:
            infeasible = True
            break


    while(potC):    
        new_team = potC.pop(random.randint(0,len(potC)-1))


        while(len(Copps[new_team]) < 2 and len(possible_Copps[new_team]) > 0):        
            new_opponent = random.choice(possible_Copps[new_team])
            if len(Copps[new_opponent]) < 2:            
                if check_feasible(fixed_games+[(new_team,new_opponent)]):                
                    Copps[new_team].append(new_opponent)
                    Copps[new_opponent].append(new_team)
                    fixed_games.append((new_team,new_opponent))
                possible_Copps[new_team].remove(new_opponent)            
            else:
                possible_Copps[new_team].remove(new_opponent)    

        if len(Copps[new_team]) < 2:
            infeasible = True
            break

        while(len(Dopps[new_team]) < 2 and len(possible_Dopps[new_team]) > 0):        
            new_opponent = random.choice(possible_Dopps[new_team])
            if len(Copps[new_opponent]) < 2:            
                if check_feasible(fixed_games+[(new_team,new_opponent)]):                
                    Dopps[new_team].append(new_opponent)
                    Copps[new_opponent].append(new_team)
                    fixed_games.append((new_team,new_opponent))
                possible_Dopps[new_team].remove(new_opponent)            
            else:
                possible_Dopps[new_team].remove(new_opponent)    

        if len(Dopps[new_team]) < 2:
            infeasible = True
            break



    while(potD):    
        new_team = potD.pop(random.randint(0,len(potD)-1))

        while(len(Dopps[new_team]) < 2 and len(possible_Dopps[new_team]) > 0):        
            new_opponent = random.choice(possible_Dopps[new_team])
            if len(Dopps[new_opponent]) < 2:            
                if check_feasible(fixed_games+[(new_team,new_opponent)]):                
                    Dopps[new_team].append(new_opponent)
                    Dopps[new_opponent].append(new_team)
                    fixed_games.append((new_team,new_opponent))
                possible_Dopps[new_team].remove(new_opponent)            
            else:
                possible_Dopps[new_team].remove(new_opponent)    

        if len(Dopps[new_team]) < 2:
            infeasible = True
            break

    if infeasible:
        print("PROBLEM!!!!!")
        exit()
    else:
        with open('verteilung_draw2.csv', "a") as f:
            for t in teams.order_by('pot'):
                f.write(f"{i},{t.id},{';'.join([str(a) for a in Aopps[t.id]])},{';'.join([str(a) for a in Bopps[t.id]])},{';'.join([str(a) for a in Copps[t.id]])},{';'.join([str(a) for a in Dopps[t.id]])}\n")















# %%
