import time
import traceback

from celery.schedules import crontab
from celery.task import periodic_task
from django.db.models import Value, Q, Subquery, Count, IntegerField, Sum, OuterRef
from django.db.models.functions import Coalesce
from django.utils import timezone

from accounts.models import ItemPack, FifaAccountRequest, Console, ConsoleBotSetting, FifaAccount
from futplus.celery_conf import app
from sbc.public_methods import new_print, delete_sold_items, sell_items, clim_lvlup_objectives_milestones, update_account_squad_battle_data, pin_events_creator, set_sbc_status
from accounts.web_login_utils import logout_login
from sbc.sbc_solver import SBCSolver
from squad_battle.models import SquadBattleServerSideHandlerWorker, FifaAccountSquadGame
from utils.ea_settings import fifa_23_address


@app.on_after_finalize.connect
def squad_periodic_tasks(sender, **kwargs):
    print('squad battle periodic_tasks started')
    sender.add_periodic_task(120.0, squad_battle_handler_server_side_manager.s(), name='squad battle handler manager')


@app.task
def squad_battle_handler_server_side_manager():
    max_concurrency = ConsoleBotSetting.objects.get(name='celery_max_concurrency').int_value
    active_concurrency = ConsoleBotSetting.objects.get(name='celery_active_concurrency').int_value
    if active_concurrency + 12 < max_concurrency:
        handler_worker = SquadBattleServerSideHandlerWorker.objects.filter(is_done=0, has_error=0, status=0).first()
        if handler_worker:
            handler_worker.status = 1
            handler_worker.save()
            squad_battle_handler_server_side.delay(handler_worker.id)
    else:
        return 'max concurrency'


@app.task(bind=True)
def squad_battle_handler_server_side(self, squad_handler_worker_id):
    worker = SquadBattleServerSideHandlerWorker.objects.get(id=squad_handler_worker_id)
    try:
        sbc_solver = SBCSolver(worker.sbc_worker.id, worker.fifa_account.id, worker.fifa_account.user_name,
                               worker.fifa_account.password, worker.fifa_account.platform, manual_loyal=False,
                               use_public_moves=False)
        new_print(sbc_solver.fifa_account, 'start bot in squad battle server side handler')
        sbc_solver.sbc_worker.refresh_from_db()
        sbc_solver.sbc_worker.status = 'squad server side ...'
        sbc_solver.sbc_worker.status_change_time = timezone.localtime()
        sbc_solver.sbc_worker.save()
        # try:
        #     logout_login(sbc_solver, sbc_solver.sbc_worker, sbc_solver.fifa_account, create_club=True)
        #     new_print(sbc_solver.fifa_account, 'login was success')
        #     try:
        #         sbc_solver.create_club_web_app()
        #         new_print(sbc_solver.fifa_account, 'create club success')
        #     except:
        #         new_print(sbc_solver.fifa_account, 'can not create club : ', traceback.format_exc())
        #     login_status = logout_login(sbc_solver, sbc_solver.sbc_worker, sbc_solver.fifa_account)
        # except:
        #     new_print(sbc_solver.fifa_account, 'try to login but faild. try to create club')
        #     time.sleep(5)
        #     login_status = logout_login(sbc_solver, sbc_solver.sbc_worker, sbc_solver.fifa_account)
        #     if login_status and str(login_status).startswith('Traceback'):
        #         new_print(sbc_solver.fifa_account, 'error in login : , ', traceback.format_exc())
        #         worker.refresh_from_db()
        #         worker.status = 'login failed'
        #         worker.last_run_time = timezone.localtime()
        #         worker.save()
        try_update_credit = 0
        try_clim_squad_prize = 0
        try_preview_gold_pack = 0
        try_open_free_packs = 0
        try_delete_sold_items = 0
        try_sell_items = 0
        for nm in range(12):
            # bellow break for manual break loop
            worker.refresh_from_db()
            if worker.has_error:
                new_print(sbc_solver.fifa_account, 'worker has error , break it.')
                break

            if sbc_solver.sbc_worker.must_done:
                new_print(sbc_solver.fifa_account, 'worker must stop , break it.')
                break

            login_status = logout_login(sbc_solver, sbc_solver.sbc_worker, sbc_solver.fifa_account)
            # if login_status and str(login_status).startswith('Traceback'):
            if login_status.get('status_bool') is False:
                new_print(sbc_solver.fifa_account, 'error in login : , ', login_status)
                if login_status.get('reason') == 'console login':
                    set_sbc_status(self.sbc_worker, 'console login')
                    time.sleep(100)
                else:
                    # new_print(sbc_solver.fifa_account, 'error in login : , ', traceback.format_exc())
                    set_sbc_status(self.sbc_worker, 'login failed')
                # worker.refresh_from_db()
                # worker.status = 'login failed'
                # worker.last_run_time = timezone.localtime()
                # worker.save()

            time.sleep(2)
            if try_update_credit < 2:
                try:
                    sbc_solver.update_credit()
                    try_update_credit += 2
                except:
                    new_print(sbc_solver.fifa_account, 'update credit error : ', traceback.format_exc())
                    try_update_credit += 1
                    continue
                    # if nm < 3:
                    #     new_print(sbc_solver.fifa_account, 'try again : ', nm)
                    #     continue
            time.sleep(2)
            if try_clim_squad_prize < 2:
                try:
                    # if nm < 4:
                    # clim squad battle reward
                    resp = sbc_solver.se.post(
                        f'{fifa_23_address}/sqbt/user/prize',
                        headers=sbc_solver.main_dic['header'])
                    new_print(sbc_solver.fifa_account, 'sqbt content = ', resp.text)
                    try_clim_squad_prize += 2
                except:
                    new_print(sbc_solver.fifa_account, 'error 22 : ', traceback.format_exc())
                    try_clim_squad_prize += 1
                    continue
                    # if nm < 4:
                    #     new_print(sbc_solver.fifa_account, 'try again : ', nm)
                    #     continue
            try:
                # if nm < 2:
                # sbc_solver.sbc_worker.refresh_from_db()
                # sbc_solver.sbc_worker.status_change_time = timezone.localtime()
                # sbc_solver.sbc_worker.status = 'clim objectives and milestones ...'
                # sbc_solver.sbc_worker.save()
                clim_count_in_24_hour = FifaAccountRequest.objects.using('logs').filter(
                    fifa_account=sbc_solver.fifa_account,
                    link=f'{fifa_23_address}/scmp/campaign?type=active',
                    create_time__gt=timezone.localtime() - timezone.timedelta(hours=24)
                ).count()
                if clim_count_in_24_hour < 1:
                    # clim objectives and milestones
                    pin_events_creator(
                        fifa_account=sbc_solver.fifa_account,
                        se=sbc_solver.se, persona_id=sbc_solver.fifa_account.persona_id,
                        nucleus_id=sbc_solver.fifa_account.nucleus_id,
                        source="Objectives - Season Progress", sid=sbc_solver.sid,
                        counter=sbc_solver.get_pin_events_counter())
                    clim_lvlup_objectives_milestones(
                        sbc_solver.fifa_account, sbc_solver.se, sbc_solver.main_dic['header'], sbc_solver.fcas_sid,
                    )
                    time.sleep(2)
                    sell_items(sbc_solver, sbc_solver.sbc_worker, sbc_solver.fifa_account)
                else:
                    new_print(sbc_solver.fifa_account, f'climed in 24 hour {clim_count_in_24_hour}')

            except:
                new_print(sbc_solver.fifa_account, 'error 21 : ', traceback.format_exc())
                # if nm < 2:
                #     new_print(sbc_solver.fifa_account, 'try again : ', nm)
                #     continue
            # open preview gold pack
            if try_preview_gold_pack < 2:
                try:
                    # if nm < 6:
                    sbc_solver.preview_and_open_good_gold_pack()
                    try_preview_gold_pack += 2
                except:
                    new_print(sbc_solver.fifa_account, 'can not open gold pack error : ', traceback.format_exc())
                    try_preview_gold_pack += 1
                    continue
                    # if nm < 6:
                    #     continue
            time.sleep(2)
            if try_open_free_packs < 2:
                # open all free packs ( all packs reward )
                try:
                    resp = sbc_solver.se.get(
                        f'{fifa_23_address}/store/purchaseGroup/cardpack?ppInfo=true',
                        headers=sbc_solver.main_dic['header'])
                    json_resp = resp.json()
                    packs = json_resp['purchase']
                    try_open_free_packs += 2
                except:
                    new_print(sbc_solver.fifa_account, 'open pack has error : ', traceback.format_exc())
                    packs = []
                    try_open_free_packs += 1
                    continue
                    # if nm < 7:
                    #     new_print(sbc_solver.fifa_account, 'try again : ', nm)
                    #     continue
                time.sleep(2)
                sell_items_result = sell_items(sbc_solver, sbc_solver.sbc_worker, sbc_solver.fifa_account)
                for pack in packs:
                    if pack['coins'] == 0:
                        len_trade_pile = len(sell_items_result.get('trade_pile_auction', []))
                        new_print(sbc_solver.fifa_account, 'trade pile : ', len_trade_pile,
                                  ' pack id : ', pack['id'])
                        if len_trade_pile > 85 and pack.get('untradeable', True) is False:
                            new_print(sbc_solver.fifa_account, 'trade pile more than allowed. continue')
                            continue
                        try:
                            new_print(sbc_solver.fifa_account, 'pack id', pack['id'], 'will open')
                            open_pack_resp = sbc_solver.open_pack(pack['id'])
                            pack_items = []
                            try:
                                pack_items = open_pack_resp.json()['itemList']
                            except:
                                new_print(sbc_solver.fifa_account,
                                          'open pack in squad battle server handler, pack id : ', pack['id'],
                                          ' opened data : ', open_pack_resp.text, '\nerror: ', traceback.format_exc())
                            pack_price = sbc_solver.calculate_pack_price(pack_items=pack_items)
                            ItemPack.objects.create(
                                fifa_account=sbc_solver.fifa_account,
                                pack_id=pack['id'],
                                open_price=0,
                                pack_value=pack_price,
                            )
                            time.sleep(2)
                            # delete_sold_items(sbc_solver.se, sbc_solver.main_dic['header'], sbc_solver.server_key)
                            # time.sleep(2)
                            sell_items_result = sell_items(sbc_solver, sbc_solver.sbc_worker, sbc_solver.fifa_account,
                                                           check_transfer_list=False)
                        except:
                            new_print(sbc_solver.fifa_account, 'error on open pack squad handler',
                                      traceback.format_exc())

            # read squad battle data
            try:
                new_print(sbc_solver.fifa_account, 'updating squad battle data')
                update_account_squad_battle_data(sbc_solver.fifa_account, sbc_solver.se, sbc_solver.main_dic['header'])
            except:
                new_print(sbc_solver.fifa_account, 'error on read squad battle data : ', traceback.format_exc())
            # squad build handle
            try:
                if worker.need_fix_main_squad:
                    sbc_solver.active_squad_handler(use_contract=bool(worker.use_contract))
                else:
                    new_print(sbc_solver.fifa_account,
                              'allowed games done or not need fix main squad. no need to handle active squad')
            except:
                new_print(sbc_solver.fifa_account, 'error on active squad handler', traceback.format_exc())
                # if nm < 8:
                #     new_print(sbc_solver.fifa_account, 'try again : ', nm)
                #     continue
            worker.refresh_from_db()
            worker.need_contracts_count = sbc_solver.need_contracts
            worker.need_manager_contracts_count = sbc_solver.manager_need_contracts
            worker.need_heals_count = sbc_solver.need_heals
            worker.need_position_modifier = sbc_solver.need_position_change
            if sbc_solver.fifa_account.use_special_squad:
                worker.need_player_for_special_squad.clear()
                for player_card_item in sbc_solver.need_players_for_special_squad:
                    worker.need_player_for_special_squad.add(player_card_item)
            worker.save()
            time.sleep(2)
            # if try_delete_sold_items < 2:
            #     try:
            #         delete_sold_items(sbc_solver.se, sbc_solver.main_dic['header'], sbc_solver.server_key)
            #         time.sleep(2)
            #         try_delete_sold_items += 2
            #     except:
            #         new_print(sbc_solver.fifa_account, 'error on delete sold items . error : ', traceback.format_exc())
            #         try_delete_sold_items += 1
            #         continue
            #         # if nm < 9:
            #         #     new_print(sbc_solver.fifa_account, 'try again : ', nm)
            #         #     continue
            if try_sell_items < 2:
                try:
                    sell_items(sbc_solver, sbc_solver.sbc_worker, sbc_solver.fifa_account, sell_club_players=True)
                    # time.sleep(2)
                    # sbc_solver.update_credit()
                    try_sell_items += 2
                except:
                    new_print(sbc_solver.fifa_account, 'error sell items squad handler . error : ',
                              traceback.format_exc())
                    try_sell_items += 1
                    continue
                    # if nm < 10:
                    #     new_print(sbc_solver.fifa_account, 'try again : ', nm)
                    #     continue
            break
        new_print(sbc_solver.fifa_account, '------ handler server side is done ----')
        worker.status = 2
        worker.is_done = True
        worker.end_time = timezone.localtime()
        worker.save()
    except:
        worker.status = 3
        worker.is_done = True
        worker.has_error = True
        worker.error_description = traceback.format_exc()
        worker.end_time = timezone.localtime()
        worker.save()


@periodic_task(run_every=crontab(hour="10,15,20", minute='1'))
def update_console_dynamic_allowed_play_game_day():
    pass
    # for console in Console.objects.filter(allowed_play_game_day=Value(0)):
    #     console.allowed_play_game_day = console.fifaaccount_set.filter(
    #         Q(delete_console_reason=None) | Q(delete_console_reason=''),
    #     ).annotate(
    #         account_play_31_days=Coalesce(Subquery(FifaAccountSquadGame.objects.filter(
    #             fifa_account__id=OuterRef('pk'),
    #             create_time__gt=timezone.localtime() - timezone.timedelta(days=31)
    #         ).values('fifa_account').annotate(
    #             played_count_31_day=Count('id', output_field=IntegerField())
    #         ).values('played_count_31_day'), output_field=IntegerField()), Value(0)),
    #         account_all_played_games=Coalesce(Subquery(FifaAccountSquadGame.objects.filter(
    #             fifa_account__id=OuterRef('pk'),
    #             # create_time__gt=now_local_time - timezone.timedelta(hours=24)
    #         ).values('fifa_account').annotate(
    #             played_count_all=Count('id', output_field=IntegerField())
    #         ).values('played_count_all'), output_field=IntegerField()), Value(0)),
    #     ).filter(
    #         # account_play_31_days__lt=Value(88)
    #         (Q(account_all_played_games__lt=Value(105)) | Q(account_all_played_games__gt=Value(200)))
    #     ).aggregate(
    #         sum_account_day=Sum('allowed_play_game_day')).get('sum_account_day') or 0
    #     console.save()


@periodic_task(run_every=crontab(day_of_week=0, hour=11, minute=30))
def reset_squad():
    result = FifaAccount.objects.all().update(
        squad_special_game_two=True, squad_special_game_one=False, account_played_games=0)
    FifaAccount.objects.filter(
        console__name__in=list(range(0, 9000))
    ).update(
        allowed_play_game=0,
        allowed_play_game_day=1,
    )
    return str(result)
