import os
import random
import threading
import time
import traceback

import requests
from django.db import close_old_connections
from django.db.models import Min, Q
from django.utils import timezone

from accounts.add_new_account_to_console import AddNewAccountConsole
from accounts.models import Console, FifaAccount, FifaAccountInformation, NewConsoleAccount
from accounts.public import console_need_force_sleep
from futplus.settings import CONSOLE_NAME, BASE_DIR, CONSOLE_NAME_LIST
from sbc import ARZBAZI_REPORT_TOPIC_ADD_REMOVE_ACCOUNT_THREAD_MESSAGE_ID, ARZBAZI_REPORT_TOPIC_LOGS_THREAD_MESSAGE_ID, \
    FIFA_REPORT_TOKEN, ARZBAZI_REPORT_GROUP_ID
from sbc.login_to_ea_with_selenium import login_to_account
from sbc.models import SBCWorker, SBCFileWorkLog
from sbc.public_methods import new_print, ThreadWithReturnValue, focus_on_ps, check_bot_version, \
    get_current_connections_to_database, telegram_send_photo, set_sbc_status, set_console_status
from accounts.web_login_utils import logout_login
from sbc.sbc_solver import SBCSolver
from squad_battle.squad_battle_bot import SquadBattleRunner
from utils.public_moves import PublicMoves
from utils.realy_public_methods import telegram_send_message_fifa_account


def start_pc_bots():
    print('working bots on pc ...')
    last_account_worker = SBCWorker.objects.filter(fifa_account__console__name=CONSOLE_NAME).last()
    console_first_account = FifaAccount.objects.filter(console__name=CONSOLE_NAME).first()
    if not console_first_account:
        return
    print('sbc file , logs in ', console_first_account)
    completed_1hour = SBCWorker.objects.filter(
        Q(is_done=True) | Q(has_error=True),
        running_platform='console_web_pc',
        fifa_account__console__name=CONSOLE_NAME,
        end_time__gt=timezone.localtime() - timezone.timedelta(hours=1)
    ).count()
    max_work_count = len(CONSOLE_NAME_LIST) * 12
    if completed_1hour >= max_work_count:
        print(f'more than {max_work_count} account completed in last hour, dont work file now. step one')
        return
    need_run_console_web_pc = SBCWorker.objects.filter(
        is_done=False, has_error=False, must_done=False, running_platform='console_web_pc',
        fifa_account__console__name__in=CONSOLE_NAME_LIST
    ).count()
    new_print(console_first_account, 'sbc file , need run console web : ', need_run_console_web_pc)
    if need_run_console_web_pc > 0:
        new_print(console_first_account, 'close old console workers')
        SBCWorker.objects.filter(
            is_done=False, has_error=False, must_done=False, running_platform='console',
            fifa_account__console__name__in=CONSOLE_NAME_LIST
        ).update(has_error=True, error_description='worker stopped middle of work')
    if last_account_worker and need_run_console_web_pc:
        # fifa_account = last_account_worker.fifa_account
        # public_moves = PublicMoves(None, last_account_worker, None, {},
        #                            None)
        # thread_1 = ThreadWithReturnValue(target=pc_sbc_manager, raise_on_exception=True)
        try:
            thread_1 = ThreadWithReturnValue(target=pc_sbc_manager, raise_on_exception=True)
            # threading.Thread(target=focus_on_ps,
            #                  kwargs={'fifa_account': fifa_account}).start()
            close_old_connections()
            thread_1.start()
            time.sleep(10)
            while thread_1.is_alive():
                # new_print(fifa_account, 'console web pc waiting sbc solver end , pressing left and right')
                # public_moves.ps4_buttons.right()
                # time.sleep(1)
                # public_moves.ps4_buttons.left()
                time.sleep(100)
            result = thread_1.join()
            if result == 'need update bot':
                return 'need update bot'
            elif result == 'need force sleep':
                return 'need force sleep'
        except Exception as error:
            print('exception in run start_sbc_bots , ', error)
            time.sleep(20)
            close_old_connections()
            new_print(console_first_account, 'exception in run start_sbc_bots ', traceback.format_exc())


def pc_sbc_manager(loop_counter=0):
    print('pc_sbc_manager started , ', timezone.localtime())
    os.system('TASKKILL /F /IM chrome.exe /FI "USERNAME eq %username%"')
    time.sleep(5)
    if check_bot_version() == 'need update bot':
        return 'need update bot'
    if console_need_force_sleep(running_platform='console_web_pc'):
        return 'need force sleep'
    completed_1hour = SBCWorker.objects.filter(
        Q(is_done=True) | Q(has_error=True),
        running_platform='console_web_pc',
        fifa_account__console__name__in=CONSOLE_NAME_LIST,
        end_time__gt=timezone.localtime() - timezone.timedelta(hours=1)
    ).count()
    max_work_count = len(CONSOLE_NAME_LIST) * 12
    if completed_1hour >= max_work_count:
        print(f'more than {max_work_count} account completed in last hour, dont work file now. step 2')
        return
        # sbc_worker = SBCWorker.objects.filter(
    #     is_done=False, has_error=False, must_done=False, running_platform='console_web_pc',
    #     last_run_time__lt=timezone.localtime() - timezone.timedelta(hours=1, minutes=2),
    #     status_change_time__lt=timezone.localtime() - timezone.timedelta(minutes=20),
    #     # fifa_account__active=False,
    #     fifa_account__console__name=CONSOLE_NAME
    # ).order_by('last_run_time').first()
    console_first_account = FifaAccount.objects.filter(console__name=CONSOLE_NAME).first()
    max_multiple_accounts = (len(CONSOLE_NAME_LIST) * 12) - completed_1hour
    if max_multiple_accounts > 18:
        max_multiple_accounts = 18

    new_print(console_first_account, f'want to run multiple accounts {max_multiple_accounts}')
    if max_multiple_accounts <= 0:
        new_print(console_first_account, f'too many account worked in 1hour ago {completed_1hour}')
        close_old_connections()
        return
    min_times = SBCWorker.objects.filter(
        is_done=False, has_error=False, must_done=False, running_platform='console_web_pc',
        last_run_time__lt=timezone.localtime() - timezone.timedelta(hours=1, minutes=2),
        status_change_time__lt=timezone.localtime() - timezone.timedelta(minutes=20),
        fifa_account__console__name__in=CONSOLE_NAME_LIST
    ).values('fifa_account_id').annotate(
        min_last_run_time=Min('last_run_time')
    ).order_by('min_last_run_time')[:max_multiple_accounts]
    if not min_times:
        sbc_workers = SBCWorker.objects.none()
    else:
        filter_q = Q()
        for row in min_times:
            filter_q |= Q(fifa_account_id=row['fifa_account_id'], last_run_time=row['min_last_run_time'])
        sbc_workers = SBCWorker.objects.filter(filter_q)
    current_connections_count = get_current_connections_to_database('logs_pgbouncer')
    thread_list = []
    if sbc_workers and current_connections_count < 8000:
        new_print(console_first_account, 'run console_web_pc workers for ', len(list(sbc_workers)))
        # print(f'run sbc worker in console for {sbc_worker.fifa_account.user_name} , start time : {timezone.localtime()}')
        if len(CONSOLE_NAME_LIST) > 1 and sbc_workers:
            sbc_workers_console_names = sbc_workers.values_list('fifa_account__console__name', flat=True)
            for console_obj in Console.objects.filter(
                name__in=CONSOLE_NAME_LIST,
            ).exclude(
                name__in=sbc_workers_console_names
            ):
                set_console_status(console=console_obj, status='Waiting for sbc', update_time=False)
                time.sleep(2)
        sbc_file_work_log = SBCFileWorkLog.objects.create(
            console=console_first_account.console,
            work_type='sbc',
            work_status='start',
            work_accounts_count=len(list(sbc_workers)),
            start_time=timezone.localtime(),
        )
        for sbc_worker in sbc_workers:
            new_print(console_first_account, f'run sbc worker in console for {sbc_worker.fifa_account.user_name} ,'
                  f' start time : {timezone.localtime()} ,'
                  f' current connections {current_connections_count}')
            thread_item = ThreadWithReturnValue(target=active_sbc_bot, kwargs={'uncompleted_worker_id': sbc_worker.id})
            thread_list.append(thread_item)
            thread_item.start()
            time.sleep(10)
            new_print(console_first_account, f'thread run for {sbc_worker.fifa_account.user_name} , start time : {timezone.localtime()}')
        close_old_connections()
        for running_thread in thread_list:
            running_thread.join()
        close_old_connections()
        sbc_file_work_log.end_time = timezone.localtime()
        sbc_file_work_log.work_status = 'end'
        sbc_file_work_log.save(update_fields=['end_time', 'work_status'])
        # active_sbc_bot(sbc_worker.id)
        return pc_sbc_manager(loop_counter=loop_counter+1)
    elif current_connections_count >= 8000:
        new_print(console_first_account, f'too many opened connections {current_connections_count}')
        close_old_connections()
        time.sleep(120)
        return
    else:
        SBCWorker.objects.filter(
            is_done=False, has_error=False, must_done=False, running_platform='console_web_pc',
            fifa_account__console__name__in=CONSOLE_NAME_LIST,
            status_change_time__lt=timezone.localtime() - timezone.timedelta(hours=1, minutes=20),
        ).update(has_error=True, error_description='worker stopped middle of work')


def start_get_backup_code():
    print('working on get_backup_code on pc')
    need_run_console_web_pc = SBCWorker.objects.filter(
        is_done=False, has_error=False, must_done=False, running_platform='console_web_backup_code',
        fifa_account__console__name__in=CONSOLE_NAME_LIST
    )
    need_backup_count = need_run_console_web_pc.count()
    print('need backup code for ', need_backup_count)
    if need_backup_count:
        for item in need_run_console_web_pc:
            uncompleted_worker = SBCWorker.objects.filter(id=item.id).first()
            uncompleted_worker.task_id = '1'
            uncompleted_worker.last_run_time = timezone.localtime()
            uncompleted_worker.must_done = False
            uncompleted_worker.save()

            fifa_account = uncompleted_worker.fifa_account
            new_print(fifa_account, 'start console back up code ', timezone.localtime())
            # if not fifa_account.active:
            fifa_account.need_captcha = 0
            fifa_account.active = True
            fifa_account.last_run_time = timezone.localtime()
            fifa_account.save(update_fields=['need_captcha', 'active', 'last_run_time'])

            sbc_solver_item = SBCSolver(uncompleted_worker.id, fifa_account.id, fifa_account.user_name,
                                        fifa_account.password,
                                        fifa_account.platform, uncompleted_worker.manual_loyal)
            backup_code_obj = fifa_account.account_backup_code.last()
            if not backup_code_obj:
                new_print(fifa_account, 'bot stop , no app code found')
                return
            login_selenium_result = login_to_account(
                sbc_solver_item.username, sbc_solver_item.password, None,
                proxy_ip=None, proxy_port=None, proxy_user=None, proxy_pass=None,
                app_auth=backup_code_obj.app_code, cookies=fifa_account.selenium_cookies)
            if login_selenium_result.get('status_bool') is True:
                driver = login_selenium_result.get('driver')
                backup_codes = sbc_solver_item.get_new_backup_codes(driver, None, backup_code_obj.app_code)
                backup_code_obj.backup_code = backup_codes[0]
                backup_code_obj.backup_codes_str = ','.join(backup_codes)
                backup_code_obj.save(update_fields=['backup_codes_str', 'backup_code'])
                uncompleted_worker.is_done = True
                uncompleted_worker.save(update_fields=['is_done'])
                driver.quit()
            else:
                new_print(fifa_account, 'cant login 1 , error : ', traceback.format_exc())
                uncompleted_worker.has_error = True
                uncompleted_worker.error_description = login_selenium_result.get('error')
                uncompleted_worker.save(update_fields=['has_error', 'error_description'])


# def pc_sbc_manager(loop_counter=0):
#     print('pc_sbc_manager started , ', timezone.localtime())
#     if check_bot_version() == 'need update bot':
#         return 'need update bot'
#     sbc_workers = SBCWorker.objects.filter(
#         is_done=False, has_error=False, must_done=False, running_platform='console_web_pc',
#         last_run_time__lt=timezone.localtime() - timezone.timedelta(hours=1, minutes=2),
#         status_change_time__lt=timezone.localtime() - timezone.timedelta(minutes=20),
#         # fifa_account__active=False,
#         fifa_account__console__name=CONSOLE_NAME
#     ).order_by('last_run_time')
#     sbc_workers = list(sbc_workers)
#     current_connections_count = get_current_connections_to_database()
#     thread_list = []
#     workers_counter = 0
#     while True:
#         if len(thread_list) >= 3:
#             for worker in thread_list:
#                 if not worker.is_alive():
#                     thread_list.remove(worker)
#             time.sleep(10)
#             continue
#         if len(sbc_workers) > workers_counter:
#             one_worker = sbc_workers[workers_counter]
#         else:
#             one_worker = None
#         if one_worker and current_connections_count < 2400:
#             print(f'run sbc worker in console for {one_worker.fifa_account.user_name} ,'
#                   f' start time : {timezone.localtime()} ,'
#                   f' current connections {current_connections_count}')
#             workers_counter += 1
#             thread_item = ThreadWithReturnValue(target=active_sbc_bot, kwargs={'uncompleted_worker_id': one_worker.id})
#             thread_list.append(thread_item)
#             thread_item.start()
#             print(f'thread run for {one_worker.fifa_account.user_name} , start time : {timezone.localtime()}')
#             close_old_connections()
#             time.sleep(10)
#             continue
#         elif current_connections_count >= 2400:
#             print(f'too many opened connections {current_connections_count}')
#             time.sleep(100)
#             continue
#         elif len(thread_list) == 0:
#             SBCWorker.objects.filter(
#                 is_done=False, has_error=False, must_done=False, running_platform='console_web_pc',
#                 fifa_account__console__name=CONSOLE_NAME,
#                 status_change_time__lt=timezone.localtime() - timezone.timedelta(hours=1, minutes=20),
#             ).update(has_error=True, error_description='worker stopped middle of work')
#             print('no sbc_console_web worker left , back to main thread')
#             break
#         time.sleep(10)

def active_sbc_bot(uncompleted_worker_id):
    uncompleted_worker = SBCWorker.objects.filter(id=uncompleted_worker_id).first()
    uncompleted_worker.task_id = '1'
    uncompleted_worker.last_run_time = timezone.localtime()
    uncompleted_worker.must_done = False
    uncompleted_worker.save()

    fifa_account = uncompleted_worker.fifa_account
    new_print(fifa_account, 'start active_sbc_bot ', timezone.localtime(),
              ' worker id : ', uncompleted_worker_id)
    # if not fifa_account.active:
    if True:
        fifa_account.need_captcha = 0
        fifa_account.active = True
        fifa_account.last_run_time = timezone.localtime()
        fifa_account.save(update_fields=['need_captcha', 'active', 'last_run_time'])

        sbc_solver_item = SquadBattleRunner(
            uncompleted_worker.id, fifa_account.id, fifa_account.user_name,
            fifa_account.password, fifa_account.platform, uncompleted_worker.manual_loyal)
        try:
            set_sbc_status(uncompleted_worker, None)
            login_result = logout_login(sbc_solver_item, uncompleted_worker, fifa_account, )
            if (login_result.get('status_bool') is False and
                    sbc_solver_item.running_platform == 'console_web_pc'):
                if login_result.get('reason') == 'ea servers unavailable':
                    # todo : try to login again for these accounts after some hours
                    pass
                close_old_connections()
                fifa_account.refresh_from_db()
                fifa_account.active = False
                fifa_account.save(update_fields=['active'])
                uncompleted_worker.refresh_from_db()
                uncompleted_worker.has_error = True
                uncompleted_worker.error_description = str(login_result.get('reason'))
                uncompleted_worker.save(update_fields=['has_error', 'error_description'])
                return 'can`t login , leave it for now'
        except Exception as e:
            close_old_connections()
            new_print(fifa_account, 'error on sbc active bot : ', traceback.format_exc())
            # handle_end_bot()
        try:
            new_print(fifa_account, 'main_dic["persona"] = ', sbc_solver_item.persona_id)
        except:
            new_print(fifa_account, 'exception error 111 : ', traceback.format_exc())
            close_old_connections()
            raise Exception('error')
        try:
            account_information, created = FifaAccountInformation.objects.get_or_create(fifa_account=fifa_account)
            # requested_ip = requests.get('https://arzbazi.com/accounts/get-local-timezone/').json().get('request_ip')
            requested_ip = requests.get('https://checkip.amazonaws.com/').text.strip()
            new_print(fifa_account, 'requested ip : ', requested_ip, ' last ip : ', account_information.last_ip_address)
            if account_information.last_ip_address and account_information.last_ip_address != requested_ip:
                text_message = (f'last ip is not equal to requested {fifa_account} , '
                                f'request ip : {requested_ip} , '
                                f'last ip : {account_information.last_ip_address}')
                new_print(fifa_account, text_message)
                sbc_solver_item.send_message(None, 123, text_message,
                                             message_thread_id=ARZBAZI_REPORT_TOPIC_LOGS_THREAD_MESSAGE_ID)
            account_information.last_ip_address = requested_ip
            account_information.save(update_fields=['last_ip_address'])
        except:
            new_print(fifa_account, 'exception error 124 : ', traceback.format_exc())
        try:
            if fifa_account.is_running_console_trade_one or fifa_account.run_console_invest_trade_one:
                sbc_solver_item.invest_move_items_to_club()
            uncompleted_worker.start_time = timezone.localtime()
            uncompleted_worker.save(update_fields=['start_time'])
            solve_result = sbc_solver_item.sbc_solver_core()
            if solve_result in ['all sbc completed', 'no credit', 'must done', 'its done', 'max request 24 hour']:
                set_sbc_status(uncompleted_worker, solve_result, update_time=True)
                uncompleted_worker.end_time = timezone.localtime()
                uncompleted_worker.save(update_fields=['end_time'])
        except:
            new_print(fifa_account, 'exception error 112 : ', traceback.format_exc())
        fifa_account.refresh_from_db()
        fifa_account.active = False
        fifa_account.last_run_time = timezone.localtime()
        fifa_account.save(update_fields=['active', 'last_run_time'])
        if len(CONSOLE_NAME_LIST) > 1:
            set_console_status(console=fifa_account.console, status='Waiting for sbc', update_time=False)
    close_old_connections()


def start_add_and_remove_accounts(console_object: Console):
    fifa_account = FifaAccount.objects.filter(console=console_object).last()
    if not fifa_account:
        print('add one account for this console in system , log in : ', fifa_account)
        return
    sbc_worker = SBCWorker.objects.filter(
        running_platform='console_manage_accounts',
        fifa_account=fifa_account
    ).last()
    if not sbc_worker:
        sbc_worker, created = SBCWorker.objects.get_or_create(
            is_done=True, running_platform='console_manage_accounts',
            fifa_account=fifa_account,
        )
    public_moves = PublicMoves(None, sbc_worker, None, None, None)
    public_moves.console_login_utils.focus_on_ps()
    time.sleep(2)
    try:
        public_moves.just_find_state()
    except OSError as screen_grap_error:
        if 'screen grab failed' in str(screen_grap_error):
            new_print(fifa_account, 'screen grap error 2 , ', screen_grap_error, ' restart pc , ',
                      ' full trace : ', traceback.format_exc())
            telegram_send_message_fifa_account(
                fifa_account, FIFA_REPORT_TOKEN,
                f'screen grap error\n account {fifa_account}\n console : {fifa_account.console}\n restarting...',
                chat_id=ARZBAZI_REPORT_GROUP_ID,
                message_thread_id=ARZBAZI_REPORT_TOPIC_LOGS_THREAD_MESSAGE_ID
            )
            set_sbc_status(sbc_worker, 'screen grap error , restarting')
            time.sleep(10)
            os.system("shutdown /r /t 1")
        else:
            raise screen_grap_error
    while True:
        console_object.refresh_from_db()
        if console_object.remove_accounts:
            for iii in range(5):
                current_stat = public_moves.just_find_state()
                if current_stat == 'ps4_main':
                    break
                else:
                    print('go to home first 2')
                    try:
                        public_moves.xboxs_go_to_app()
                        time.sleep(5)
                    except:
                        pass
                    new_print(fifa_account, 'go to home first')
                    public_moves.ps4_buttons.ps()
                    time.sleep(5)
                    public_moves.ps4_buttons.cross()
                    time.sleep(2)
            result = public_moves.console_accounts_utils.delete_accounts(delete_keys=False)
            print('bot result : ', result)
            new_print(fifa_account, 'remove accounts bot result : ', result)
            public_moves.get_screen_shot()
            telegram_send_photo(fifa_account, os.path.join(BASE_DIR, 'screen_shot_tmp.jpg'),
                                caption=f'console {console_object.name} accounts removed',
                                message_thread_id=ARZBAZI_REPORT_TOPIC_ADD_REMOVE_ACCOUNT_THREAD_MESSAGE_ID)
            console_object.remove_accounts = False
            console_object.save(update_fields=['remove_accounts'])


        if console_object.add_accounts:
            AddNewAccountConsole().core(keep_alive=False)
            exists_items = NewConsoleAccount.objects.filter(
                Q(add_status='exists'),
                console=console_object,
                create_time__gte=timezone.localtime() - timezone.timedelta(days=10))
            list_exists_items = list(exists_items)
            if exists_items.count() > 0:
                new_print(fifa_account, 'need remove some accounts , ', exists_items)
                public_moves.console_accounts_utils.delete_accounts(delete_keys=False, delete_failed_only=True)
                NewConsoleAccount.objects.filter(
                    Q(add_status='exists'),
                    console=console_object,
                    create_time__gte=timezone.localtime() - timezone.timedelta(days=10)).update(add_status=None)
                new_print(fifa_account, 'some items removed , so try again')
                new_add_console_bot = AddNewAccountConsole()
                new_add_console_bot.exists_accounts = list_exists_items
                new_add_console_bot.core(keep_alive=False)
            console_object.add_accounts = False
            console_object.save(update_fields=['add_accounts'])
        if console_object.check_accounts:
            for iii in range(5):
                current_stat = public_moves.just_find_state()
                if current_stat == 'ps4_main':
                    break
                else:
                    print('go to home first 2')
                    try:
                        public_moves.xboxs_go_to_app()
                        time.sleep(5)
                    except:
                        pass
                    new_print(fifa_account, 'go to home first')
                    public_moves.ps4_buttons.ps()
                    time.sleep(5)
                    public_moves.ps4_buttons.cross()
                    time.sleep(2)
            public_moves.console_accounts_utils.check_accounts_func()
            console_object.check_accounts = False
            console_object.save(update_fields=['check_accounts'])
        if console_object.stop_after_add_remove_accounts:
            print('pressing left and right to keep console alive after remove accounts.')
            new_print(fifa_account, 'stop after add-remove accounts active ,'
                                    ' so pressing left and right to keep console alive after remove accounts.')
            public_moves.ps4_buttons.right()
            time.sleep(1)
            public_moves.ps4_buttons.left()
            time.sleep(50)
            set_sbc_status(sbc_worker, 'stop after add accounts', update_time=False)
        else:
            break

    # if console_object.stop_after_add_remove_accounts:
    #     while True:
    #         print('pressing left and right to keep console alive after remove accounts.')
    #         new_print(fifa_account, 'stop after add-remove accounts active ,'
    #                                 ' so pressing left and right to keep console alive after remove accounts.')
    #         public_moves.ps4_buttons.right()
    #         time.sleep(1)
    #         public_moves.ps4_buttons.left()
    #         time.sleep(100)
    #         set_sbc_status(sbc_worker, 'stop after add accounts')
