# %%
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=59)

# %%

teamObjects = Team.objects.filter(season=scenario.season,active=True)
teams = teamObjects.values('id','country','pot')

getTeamById = {}
for t in teamObjects:
    getTeamById[t.id] = f"({t.pot}) {t.name}"


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

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

teams = list(teams)

def check_feasible(fixed_games):
    model = xp.problem(name='Draws', sense=xp.minimize)
    model.setControl ('outputlog', 0)

    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 in range(1,5):
            model.addConstraint(xp.Sum(x[t['id'],t2['id']] for t2 in pot[r] if (t['id'],t2['id']) in x.keys()) == 2)
        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)

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

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

    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,2):
    print("SIMULATION",i)


    possible_opps = {}
    for p in range(1,5):
        teams_from_pot = list(teamObjects.filter(pot=p).values('id','country'))
        possible_opps[p] = {
            t['id']: [t2['id'] for t2 in teams_from_pot if t2['country'] != t['country']] for t in teams
        }

    sol_opps = {
        (t['id'],p):[]
        for t in teams for p in range(1,5)
    }
    fixed_games = []
    feasible = True

    num_combinatorial_clashes = {
        (t['id'],p):0 for t in teams for p in range(8)
    }

    num_possible_opps = {
        (t['id'],p):None for t in teams for p in range(8)
    }

    draw_index = 1
    draw_index_dict = {
        t['id']:None for t in teams
    }
    
    

    for p in range(1,5): 
        currentPot = list(teamObjects.filter(pot=p).values_list('id', flat=True))

        while(feasible and currentPot):    
            # Draw Team
            new_team = currentPot.pop(random.randint(0,len(currentPot)-1))
            print("\n\n\n######")
            print(draw_index)
            print("New team",new_team,getTeamById[new_team])                        
            draw_index_dict[new_team] = draw_index
            draw_index += 1
            for pos in range(8):
                newpot = pos//2+1

                # Skip if already drawn
                if len(sol_opps[new_team,newpot]) >= 2:
                    continue
                #  Update possible opponents     
                opponent_pool = []           
                for new_opponent in possible_opps[newpot][new_team]:
                    # print("Try opponent",new_opponent,getTeamById[new_opponent])                        
                    if len(sol_opps[new_opponent,p]) >= 2:
                        continue
                        # print(f"---> ALREADY DRAWN {pos}: {getTeamById[new_opponent]}")                            
                    else:
                        new_game = (new_team,new_opponent) 
                        if check_feasible(fixed_games+[new_game]):
                            opponent_pool.append(new_opponent)
                        else:
                            # print(f"---> CANNOT DRAW {pos}: {[getTeamById[ttt] for ttt in new_game]}")                            
                            num_combinatorial_clashes[new_team,pos] += 1
                num_possible_opps[new_team,pos] = len(opponent_pool)
                # Draw random opponent                
                new_opponent = random.choice(opponent_pool)
                new_game = (new_team,new_opponent)
                print(pos,num_possible_opps[new_team,pos],getTeamById[new_opponent])
                # print("New team",new_opponent,getTeamById[new_opponent])                        
                # exit()

                # if not check_feasible(fixed_games+[new_game]):
                #     print("INFEASIBLE")
                #     print("POSSOPS AFTER",[getTeamById[tt] for tt in possible_opps[pos][new_team]])
                #     print(f"{p} - {pos} - {getTeamById[sol_opps[new_opponent,(2*p-1)-(pos % 2)]]}")
                #     feasible = False
                #     break
                    
                sol_opps[new_team,newpot].append(new_opponent)
                sol_opps[new_opponent,p].append(new_team)
                # if pos % 2 == 0:
                possible_opps[newpot][new_team].remove(new_opponent)                               
                fixed_games.append(new_game)

    # with open('draw_options_noHA_3country.csv', "a") as f:
    #     for t in teams:
    #         opponents = ""
    #         for p in range(1,5):
    #             for o in sol_opps[t['id'],p]:
    #                 opponents  += str(o)+';'
    #         opponents = opponents[:-1]
    #         f.write(f"{i},{t['id']},{draw_index_dict[t['id']]},{opponents},{';'.join([str(num_possible_opps[t['id'],p]) for p in range(8)])},{';'.join([str(num_combinatorial_clashes[t['id'],p]) for p in range(8)])}\n")






            # for game in fixed_games:
            #     if game[0] == new_team or game[1] == new_team:
            #         print("\t"," vs ".join([getTeamById[tt] for tt in game]))
            # print(f"--- > {draw_index_dict[new_team]}")
            # for pos in range(8):
            #     print(f"--- > {pos} - {num_possible_opps[new_team,pos]}")

            # iterate over h/a-encounters
            # for current_pos in range(8):
            #     while (sol_opps[new_team,current_pos] == None):
            #         # print(possible_opps[current_pos][new_team])
            #         #  draw opponent
            #         new_opponent = random.choice(possible_opps[current_pos][new_team])
            #         print("\tNEW OPP",new_opponent,getTeamById[new_opponent])
            #         new_game = (new_team,new_opponent) if current_pos % 2 == 0 else (new_opponent,new_team)
            #         if check_feasible(fixed_games+[new_game]):
            #             # print("FEASIBLE",new_game,[getTeamById[tt] for tt in new_game])
            #             sol_opps[new_team,current_pos] = new_opponent
            #             sol_opps[new_opponent,(2*p-1)-(current_pos % 2)] = new_team
            #             fixed_games.append(new_game)
            #             possible_opps[current_pos][new_team].remove(new_opponent)
            #         else:                      
            #             possible_opps[current_pos][new_team].remove(new_opponent)     
# %%


# # SOLUTION
# for g in fixed_games:
#     print(getTeamById[g[0]],getTeamById[g[1]])















# %%
