import threading
import time
import traceback

from django.db.models import Subquery, OuterRef, Count, IntegerField, Value
from django.db.models.functions import Coalesce
from django.utils import timezone

from accounts.models import MuleAccounts, CloseWebAppSnipes, CloseWebAppTransfers, FifaAccountSearch, ConsoleBotSetting
from sbc.models import SBCWorker
from sbc.public_methods import get_next_price_grade, new_print
from accounts.web_login_utils import logout_login
from sniper.discharge import DischargeRunner


def run_discharge_snipe(transfer: CloseWebAppSnipes):
    workers = []
    start_time = timezone.localtime()
    while len(workers) < 6 and transfer.error is False:
        transfer.refresh_from_db()
        new_print(transfer.first_account, 'discharge snipe len workers : ', len(workers))
        if start_time < timezone.localtime() - timezone.timedelta(minutes=20):
            new_print(transfer.first_account, 'more than 20 minute wait for worker. so stop it')
            # transfer.refresh_from_db()
            transfer.error = True
            transfer.error_desc = 'more than 15 min'
            transfer.save(update_fields=['error', 'error_desc'])
            for sbc_solver_item in workers:
                MuleAccounts.objects.filter(fifa_account=sbc_solver_item.fifa_account).update(in_use=False)
            return 'transfer error 3'
        worker = get_worker(transfer)
        if not worker:
            # print('no worker found')
            new_print(transfer.first_account, 'no worker found')
            time.sleep(120)
            continue
        worker.last_run_time = timezone.localtime()
        worker.must_done = False
        worker.save()
        # worker.fifa_account.last_run_time = timezone.localtime()
        # worker.fifa_account.save()

        fifa_account = worker.fifa_account
        # if not fifa_account.active:
        # if True:
        fifa_account.refresh_from_db()
        fifa_account.last_run_time = timezone.localtime()
        fifa_account.need_captcha = 0
        fifa_account.active = 1
        fifa_account.save(update_fields=['last_run_time', 'need_captcha', 'active'])

        sbc_solver_item = DischargeRunner(
            worker.id, fifa_account.id, fifa_account.user_name,
            fifa_account.password, fifa_account.platform, worker.manual_loyal, use_public_moves=False)
        try:
            login_result = logout_login(sbc_solver_item, worker, fifa_account, )
            if login_result.get('status_bool') is False:
                MuleAccounts.objects.filter(fifa_account=worker.fifa_account, error=True,
                                            error_description=login_result).update(in_use=False)
                continue
            # print(login_result)
            # print('after login')
            new_print(transfer.first_account, 'run discharge after login : ', login_result)
        except Exception as e:
            new_print(transfer.first_account, 'error 33 : ', traceback.format_exc())
            MuleAccounts.objects.filter(fifa_account=worker.fifa_account).update(in_use=False)
            continue
            # handle_end_bot()
        fifa_account.refresh_from_db()
        # print('fifa_account.active', fifa_account.active)
        new_print(transfer.first_account, 'fifa account active')
        if not fifa_account.active:
            MuleAccounts.objects.filter(fifa_account=worker.fifa_account).update(in_use=False)
            continue
        if not sbc_solver_item.web_app_discharge_with_snipe_first:
            # print('account first things failed')
            new_print(transfer.first_account, 'account first things failed')
            MuleAccounts.objects.filter(fifa_account=worker.fifa_account).update(in_use=False)
            continue
        workers.append(sbc_solver_item)
        transfer.refresh_from_db()
        new_print(transfer.first_account, 'worker append')
    if transfer.error is True:
        for sbc_solver_item in workers:
            MuleAccounts.objects.filter(fifa_account=sbc_solver_item.fifa_account).update(in_use=False)
        return 'transfer error'
    all_accounts = ''
    for sbc_solver_item in workers:
        all_accounts += str(sbc_solver_item.fifa_account.id) + ' '
    transfer.refresh_from_db()
    transfer.all_accounts_ids = all_accounts
    transfer.second_side_ready = True
    transfer.second_side_ready_time = timezone.localtime()
    transfer.save(update_fields=['all_accounts_ids', 'second_side_ready', 'second_side_ready_time'])
    new_print(transfer.first_account, 'second side ready')
    second_side_ready_time = timezone.localtime()
    while True:
        transfer.refresh_from_db()
        if transfer.first_side_done:
            break
        if transfer.error is True:
            for sbc_solver_item in workers:
                MuleAccounts.objects.filter(fifa_account=sbc_solver_item.fifa_account).update(in_use=False)
            return 'transfer error'
        if second_side_ready_time < timezone.localtime() - timezone.timedelta(minutes=10):
            new_print(transfer.first_account, 'waiting more than 10 minute for first side done')
            for sbc_solver_item in workers:
                MuleAccounts.objects.filter(fifa_account=sbc_solver_item.fifa_account).update(in_use=False)
            transfer.error = True
            transfer.error_desc = 'more than 15 min'
            transfer.save()
            return 'transfer error 2'
        time.sleep(0.1)
    time.sleep(.1)
    done = False
    count = 0
    micr_reduction_count = 1
    thread_list = []
    while True:
        account_number = 0
        for sbc_solver_item in workers:
            transfer.refresh_from_db()
            if transfer.second_side_done or transfer.error:
                done = True
                break
            #  todo : change thread to celery task
            th = threading.Thread(target=sbc_solver_item.web_app_discharge_with_snipe, kwargs={
                'transfer': transfer, 'count': count, 'account_number': account_number,
                'micr_reduction_count': micr_reduction_count, 'first_account': transfer.first_account
            })
            th.daemon = True
            th.start()
            thread_list.append(th)
            time.sleep(0.07)
            account_number += 1
            micr_reduction_count += 1
        if done:
            break
        count += 1
        if count >= 5:
            break
        # if count > 2:
        #     time.sleep(3)
        # time.sleep(1)
        # break
    # print('set accounts in_use to false')
    time.sleep(15)
    new_print(transfer.first_account, 'set accounts in_use to false')
    for sbc_solver_item in workers:
        MuleAccounts.objects.filter(fifa_account=sbc_solver_item.fifa_account).update(in_use=False)
        try:
            sbc_solver_item.main_dic['driver'].close()
        except:
            pass
        sbc_solver_item.fifa_account.refresh_from_db()
        sbc_solver_item.fifa_account.active = False
        sbc_solver_item.fifa_account.save()
    del workers
    new_print(transfer.first_account, 'transfer job is done  .... ')
    # print('transfer job is done ...')
    transfer.refresh_from_db()
    if not transfer.second_side_done:
        transfer.error = True
        transfer.error_desc = 'missed'
        transfer.save()
        new_print(transfer.first_account, 'transfer missed')
    time.sleep(10)
    for thread in thread_list:
        thread.join()
        del thread


def get_worker(transfer):
    new_print(transfer.first_account, 'get workers , need a worker')
    # print('need a worker')
    # buy_now_price = self.get_next_price_grade(self.get_next_price_grade(self.get_next_price_grade(self.get_next_price_grade(self.get_next_price_grade(
    #     self.get_next_price_grade(self.get_next_price_grade(self.get_next_price_grade(self.get_next_price_grade(self.get_next_price_grade(
    #         self.get_next_price_grade(self.get_next_price_grade(self.get_next_price_grade(self.get_next_price_grade(self.get_next_price_grade(transfer.player_min_price_from_futbin)))))
    #     )))))
    # )))))
    # buy_now_price = get_previous_price_grade(get_previous_price_grade(
    #     get_previous_price_grade(transfer.player_price)))
    gn = get_next_price_grade
    buy_now_price = gn(gn(gn(gn(gn(gn(transfer.player_min_price_from_futbin))))))

    accounts = MuleAccounts.objects.filter(
        fifa_account__credit__gt=buy_now_price, in_use=False, error=False,
        fifa_account__need_captcha=False,
        investors__in=[transfer.first_account.console.investor],
        deleted=False,
    ).annotate(
        search_count_24_hour=Coalesce(Subquery(
            FifaAccountSearch.objects.filter(
                fifa_account__id=OuterRef('fifa_account__id'),
                search_time__gt=timezone.localtime() - timezone.timedelta(hours=24)
            ).values('fifa_account').annotate(
                search_counter=Count('id', output_field=IntegerField())
            ).values('search_counter'), output_field=IntegerField()
        ), Value(0))
    ).filter(
        search_count_24_hour__lt=ConsoleBotSetting.objects.get(name='account_high_search_count').int_value - 4
    ).order_by('last_used')
    for account in accounts:
        new_print(transfer.first_account, 'want to create worker with : ', account.fifa_account)
        sbc_worker = SBCWorker.objects.filter(
            fifa_account=account.fifa_account, running_platform='inject').first()
        if not sbc_worker:
            sbc_worker, created = SBCWorker.objects.get_or_create(
                fifa_account=account.fifa_account, running_platform='inject')
        sbc_worker.first_xi = True
        sbc_worker.puzzle_master = True
        sbc_worker.manual_loyal = True
        sbc_worker.save()
        if sbc_worker.has_error:
            account.error = True
            account.error_description = sbc_worker.error_description
            account.save()
        else:
            new_print(transfer.first_account,
                      'account = ', account.fifa_account.user_name, 'will participate in ', transfer.player_price)
            account.last_used = timezone.localtime()
            account.in_use = True
            account.save()
            new_print(transfer.first_account, 'worker id = ', sbc_worker.id)
            return sbc_worker


def run_worker_inject(worker_id, transfer: CloseWebAppTransfers):
    new_print(transfer.first_account, 'run worker inject , data : ', worker_id, transfer)
    uncompleted_worker = SBCWorker.objects.filter(id=worker_id).first()
    uncompleted_worker.last_run_time = timezone.localtime()
    uncompleted_worker.must_done = False
    uncompleted_worker.save()
    uncompleted_worker.fifa_account.last_run_time = timezone.localtime()
    uncompleted_worker.fifa_account.save()

    fifa_account = uncompleted_worker.fifa_account
    # if not fifa_account.active:
    if True:
        fifa_account.need_captcha = 0
        fifa_account.active = 1
        fifa_account.save()

        sbc_solver_item = DischargeRunner(
            uncompleted_worker.id, fifa_account.id, fifa_account.user_name,
            fifa_account.password, fifa_account.platform, uncompleted_worker.manual_loyal)
        if not fifa_account.proxy:
            try:
                login_result = logout_login(sbc_solver_item, uncompleted_worker, fifa_account, )
                if login_result.get('status_bool') is False:
                    raise Exception(login_result.get('reason'))
            except Exception as e:
                new_print(transfer.first_account, ' error 34 : ', traceback.format_exc())
                # handle_end_bot()
        fifa_account.refresh_from_db()
        new_print(transfer.first_account, 'fifa_account.active', fifa_account.active)
        if not fifa_account.active:
            transfer.web_app_is_on_it = False
            transfer.save()
            MuleAccounts.objects.filter(fifa_account=fifa_account).update(in_use=False)
            return False
        # sbc_solver_item.sbc_solver_core()
        sbc_solver_item.web_app_inject_staff(transfer=transfer)


def run_discharge_pod_snipe(transfer: CloseWebAppTransfers):
    mule_account = MuleAccounts.objects.get(fifa_account=transfer.first_account)
    sbc_worker, created = SBCWorker.objects.get_or_create(
        fifa_account=mule_account.fifa_account, running_platform='inject', has_error=False, must_done=False)
    # sbc_worker.first_xi = False
    # sbc_worker.puzzle_master = False
    # sbc_worker.manual_loyal = False
    sbc_worker.save()
    transfer.web_app_is_on_it = True
    transfer.web_app_start_time = timezone.localtime()
    transfer.save()
    new_print(transfer.second_account,
              'account = ', mule_account.fifa_account.user_name, 'will buy ', transfer.start_price,
              ' list start price : ')
    mule_account.last_used = timezone.localtime()
    mule_account.in_use = True
    mule_account.save()
    new_print(transfer.first_account, 'worker_id = ', sbc_worker.id)
    # uncompleted_worker = SBCWorker.objects.filter(id=worker_id).first()
    uncompleted_worker = sbc_worker
    uncompleted_worker.last_run_time = timezone.localtime()
    uncompleted_worker.must_done = False
    uncompleted_worker.save()
    uncompleted_worker.fifa_account.last_run_time = timezone.localtime()
    uncompleted_worker.fifa_account.save()

    # fifa_account = uncompleted_worker.fifa_account
    mule_fifa_account = mule_account.fifa_account

    new_print(mule_fifa_account, 'fifa_account_id = ', mule_fifa_account.id)
    sbc_solver_item = DischargeRunner(
        uncompleted_worker.id, mule_fifa_account.id, mule_fifa_account.user_name,
        mule_fifa_account.password, mule_fifa_account.platform, uncompleted_worker.manual_loyal)
    # sbc_solver_item.sbc_solver_core()
    login_status = logout_login(sbc_solver_item, sbc_worker, mule_fifa_account)
    new_print(mule_fifa_account, 'login result : ', login_status)
    if login_status.get('status_bool') is True:
        mule_account.fifa_account.refresh_from_db()
        if mule_account.fifa_account.trade_access == '0':
            uncompleted_worker.has_error = True
            uncompleted_worker.error_description = 'early_access'
            uncompleted_worker.must_done = True
            uncompleted_worker.save(update_fields=['must_done', 'error_description', 'has_error'])
            transfer.error = True
            transfer.error_description = 'mule early_access'
            transfer.web_app_is_on_it = False
            transfer.save(update_fields=['error', 'web_app_is_on_it'])
            mule_account.in_use = False
            mule_account.error = True
            mule_account.error_description = 'early_access'
            mule_account.save()
        else:
            sbc_solver_item.web_app_discharge_mode3_staff(transfer=transfer)
    else:
        uncompleted_worker.has_error = True
        uncompleted_worker.error_description = str(login_status.get('reason'))
        uncompleted_worker.must_done = True
        uncompleted_worker.save(update_fields=['must_done', 'error_description', 'has_error'])
        transfer.error = True
        transfer.error_description = login_status.get('reason') or 'mule login failed'
        transfer.web_app_is_on_it = False
        transfer.save(update_fields=['error', 'error_description', 'web_app_is_on_it'])
        if login_status.get('reason') in ['web transfer ban', 'early access done']:
            mule_account.last_used = timezone.localtime()
            mule_account.in_use = False
            mule_account.error = True
            mule_account.error_description = str(login_status.get('reason'))
            mule_account.save()


    time.sleep(2)