import os
import time

import telepot
from celery.schedules import crontab
from celery.task import periodic_task
from django.db import connections, OperationalError
from django.db.models import Count, Q, F, Sum
from django.utils import timezone
from django_redis import get_redis_connection
from paramiko.client import SSHClient
from scp import SCPClient

from accounts.models import FifaAccountCreditLog, FifaAccount, CloseWebAppSnipes, Console, FifaAccountDisable, \
    CloseWebAppTransfers, ConsoleLastStatusHistory, ConsoleLastStatus, HourlyAccountLoginLogHistory, AccountLoginLog, \
    HourlyFifaAccountRequestHistory, FifaAccountRequest, HourlyFifaAccountCreditLogHistory, TelegramMessage, ArduinoPort
from accounts.public import is_node_active
from futplus.celery_conf import app
from sbc import FIFA_REPORT_TOKEN, ARZBAZI_REPORT_GROUP_ID, ARZBAZI_REPORT_TOPIC_DISCHARGE_ERROR_THREAD_MESSAGE_ID, \
    ARZBAZI_REPORT_TOPIC_GENERAL_THREAD_MESSAGE_ID
from sniper.models import DischargeMode2, HourlyAccountDischargeHistory


@app.on_after_finalize.connect
def accounts_periodic_tasks(sender, **kwargs):
    print('accounts periodic_tasks started')
    sender.add_periodic_task(60 * 30, kill_chromes.s(), name='kill chromes', queue='can_open_chrome')
    sender.add_periodic_task(60 * 60 * 2, update_supervisor.s(), name='update supervisor', queue='can_open_chrome')
    sender.add_periodic_task(60 * 60, check_services_status.s(), name='check services')
    sender.add_periodic_task(60, update_arduino_electric_node_automate.s(), name='update arduino electric node auto')


@app.task
def kill_chromes():
    # os.system("sudo pkill -f chrome")
    os.system('pgrep -f chrome | grep -v -E "can_open_chrome|sniper kill chromes|kill_chromes" | xargs sudo kill -9')
    return


@app.task
def update_supervisor():
    # remove temps for seleniumwire
    # os.system("sudo pkill -f chrome")
    os.system('pgrep -f chrome | grep -v -E "can_open_chrome|sniper kill chromes|kill_chromes" | xargs sudo kill -9')
    time.sleep(5)
    os.system('sudo rm -rf /tmp/{,.[!.],..?}*')
    time.sleep(10)
    os.system('sudo supervisorctl restart futplus_celery_worker')
    return


@periodic_task(run_every=crontab(hour="21", minute='30'), queue='can_open_chrome')
def backup_from_database():
    os.system('nice -n 15 /usr/pgsql-11/bin/pg_dump '
              '--exclude-table=accounts_fifaaccountlog '
              '--exclude-table=accounts_fifaaccountrequest '
              '--exclude-table=accounts_telegrammessage '
              '--exclude-table=accounts_accountsalableplayercard '
              '--exclude-table=accounts_accountloginlog '
              '--exclude-table=accounts_consolelaststatushistory '
              '--exclude-table=sbc_sbcworkerdetailslog '
              '--exclude-table=sbc_sbcprocesstarget '
              '--exclude-table=sbc_sbcprocess '
              '--exclude-table=sbc_sbcsolvationmodelworker '
              '--exclude-table=trade_consoleinvesttradeitemscountlog '
              '--exclude-table=trade_consoletradeonecard '
              '-U postgres -p 5434 futplus | gzip > futplus_backup.gz')
    ssh = SSHClient()
    ssh.load_host_keys(os.path.expanduser(os.path.join("~", ".ssh", "known_hosts")))
    ssh.connect('185.110.189.222', port=1122, username='root', password='abtin@@123#CFG')
    with SCPClient(ssh.get_transport()) as scp:
        scp.put('/var/www/futplus_django/futplus_backup.gz', '/root/futplus_backup_private/futplus_backup.gz')


@periodic_task(run_every=crontab(hour="3,7,11,15,19,23", minute='30'))
def save_fifa_accounts_credit_and_discharge():
    counter = 0
    local_now = timezone.localtime()
    for item in FifaAccount.objects.exclude(
            console=None
    ).filter(last_run_time__gt=local_now - timezone.timedelta(days=31 * 2)):
        counter += 1
        # today_discharges = CloseWebAppSnipes.objects.filter(
        #     (Q(second_side_done=True) | Q(error=True)),
        #     first_account=item, insert_time__date=local_now.date(),
        # )
        # today_discharges_coin = today_discharges.annotate(
        #     transfer_coin=F('player_price') - F('player_min_price_from_futbin')
        # ).aggregate(sum_transfer_coin=Sum('transfer_coin')).get('sum_transfer_coin', 0) or 0
        # today_trades = ConsoleTradeOneHistory.objects.filter(
        #     fifa_account=item,
        #     create_time__date=local_now.date()
        # )
        # today_console_trades_profit = today_trades.exclude(
        #     Q(credit_before=0) | Q(credit_after=0)
        # ).annotate(
        #     profit_coin=F('credit_after') - F('credit_before')
        # ).aggregate(sum_profit_coin=Sum('profit_coin')).get('sum_profit_coin', 0) or 0
        # last_trade = item.consoletradeonehistory_set.filter(
        #     Q(bought_items_managed=True) | Q(list_try_count__gte=1)
        # ).order_by('create_time').last()
        try:
            last_current_items = item.fifaaccountcurrentitems
        except:
            continue
        items_forcast = 0
        items_forcast += ((last_current_items.club_bronze1_tradable_items * 200) +
                          (last_current_items.club_silver0_tradable_items * 200) +
                          (last_current_items.club_silver1_tradable_items * 300) +
                          (last_current_items.club_gold0_tradable_items * 350) +
                          (last_current_items.club_gold1_tradable_items * 650))
        # if last_trade:
        #     if last_trade.quality_name == 'bronze1':
        #         items_forcast += last_trade.transfer_list_items_count * 200
        #     if last_trade.quality_name == 'silver0':
        #         items_forcast += last_trade.transfer_list_items_count * 200
        #     if last_trade.quality_name == 'silver1':
        #         items_forcast += last_trade.transfer_list_items_count * 300
        #     if last_trade.quality_name == 'gold0':
        #         items_forcast += last_trade.transfer_list_items_count * 350
        #     if last_trade.quality_name == 'gold1':
        #         items_forcast += last_trade.transfer_list_items_count * 650
        #     if last_trade.quality_name == 'position_modifier':
        #         items_forcast += last_trade.transfer_list_items_count * 1300
        #     items_forcast += ((last_trade.club_bronze1_items * 200) +
        #                       (last_trade.club_silver0_items * 200) + (last_trade.club_silver1_items * 300) +
        #                       (last_trade.club_gold0_items * 350) + (last_trade.club_gold1_items * 650))

        # yesterday_credit = FifaAccountCreditLog.objects.filter(fifa_account=item).last()
        # today_production = 0
        # if yesterday_credit and not item.is_running_console_trade_one:
        #     today_production = item.credit - yesterday_credit.current_credit + today_discharges_coin

        discharges = None
        discharges_coin = 0
        discharges_mode2 = None
        discharges_mode2_coin = 0
        discharges_mode3 = None
        discharges_mode3_coin = 0
        last_invest_trade_salary = 0
        last_credit_log = FifaAccountCreditLog.objects.filter(fifa_account=item).last()
        if last_credit_log:
            last_invest_trade_salary = last_credit_log.today_console_trade_salary
            discharges = CloseWebAppSnipes.objects.filter(
                (Q(second_side_done=True) | Q(error=True)),
                first_account=item, insert_time__lte=local_now, insert_time__gte=last_credit_log.create_time,
            )
            discharges_coin = discharges.annotate(
                transfer_coin=F('player_price') - F('player_min_price_from_futbin')
            ).aggregate(sum_transfer_coin=Sum('transfer_coin')).get('sum_transfer_coin', 0) or 0
            discharges_mode2 = DischargeMode2.objects.filter(
                status__in=['success', 'failed'],
                fifa_account=item,
                create_time__lte=local_now, create_time__gte=last_credit_log.create_time,
            )
            discharges_mode2_coin = discharges_mode2.annotate(
                transfer_coin=F('end_price') - F('avg_price') + ((F('avg_price')*.05+F('end_price')*.05) + (F('site_price') - F('avg_price')))
            ).aggregate(sum_transfer_coin=Sum('transfer_coin')).get('sum_transfer_coin', 0) or 0
            discharges_mode3 = CloseWebAppTransfers.objects.filter(
                second_side_done=True,
                second_account=item,
                create_time__lte=local_now,
                create_time__gte=last_credit_log.create_time,
            )
            discharges_mode3_coin = discharges_mode3.annotate(
                transfer_coin=(F('buy_now_price') - F('start_price')) - (F('buy_now_price') * .05)
            ).aggregate(sum_transfer_coin=Sum('transfer_coin')).get('sum_transfer_coin', 0) or 0

        account_log = FifaAccountCreditLog.objects.create(
            create_time=local_now,
            fifa_account=item,
            # current_credit=item.credit if not item.is_running_console_trade_one else 0,
            current_credit=item.credit,
            today_discharges_coin=discharges_coin,
            today_discharges_mode2_coin=discharges_mode2_coin,
            today_discharges_mode3_coin=discharges_mode3_coin,
            # today_production=today_production,
            # today_console_trades_profit=today_console_trades_profit,
            items_forcast=items_forcast,
            today_console_trade_salary=items_forcast + item.credit,
            invest_trade_generation=((items_forcast + item.credit) - last_invest_trade_salary) + (discharges_coin + discharges_mode2_coin + discharges_mode3_coin)
        )
        if discharges:
            account_log.today_discharges.add(*discharges.values_list('id', flat=True))
        if discharges_mode2:
            account_log.today_discharges_mode2.add(*discharges_mode2.values_list('id', flat=True))
        # account_log.today_console_trades_history.add(*today_trades.values_list('id', flat=True))
        if discharges_mode3:
            account_log.today_discharges_mode3.add(*discharges_mode3.values_list('id', flat=True))
    totals = FifaAccountCreditLog.objects.filter(
        create_time__gt=local_now - timezone.timedelta(minutes=10)
    ).aggregate(
        total_current_credit=Sum('current_credit'),
        total_today_discharges_coin=Sum('today_discharges_coin'),
        total_today_discharges_mode2_coin=Sum('today_discharges_mode2_coin'),
        total_today_discharges_mode3_coin=Sum('today_discharges_mode3_coin'),
        total_today_production=Sum('today_production'),
        total_today_console_trades_profit=Sum('today_console_trades_profit'),
        total_today_console_trade_salary=Sum('today_console_trade_salary'),
        total_items_forcast=Sum('items_forcast'),
        total_invest_trade_generation=Sum('invest_trade_generation'),
    )

    HourlyFifaAccountCreditLogHistory.objects.create(
        current_credit=totals.get('total_current_credit') or 0,
        today_discharges_coin=totals.get('total_today_discharges_coin') or 0,
        today_discharges_mode2_coin=totals.get('total_today_discharges_mode2_coin') or 0,
        today_discharges_mode3_coin=totals.get('total_today_discharges_mode3_coin') or 0,
        today_production=totals.get('total_today_production') or 0,
        today_console_trades_profit=totals.get('total_today_console_trades_profit') or 0,
        today_console_trade_salary=totals.get('total_today_console_trade_salary') or 0,
        items_forcast=totals.get('total_items_forcast') or 0,
        invest_trade_generation=totals.get('total_invest_trade_generation') or 0,
    )
    return counter


@periodic_task(run_every=crontab(hour="0,1,2,3,4,5,6", minute='10,20,30,40,50'))
def update_fifa_accounts_credit_and_discharge():
    for log_item in FifaAccountCreditLog.objects.filter(
            current_credit=0, create_time__gt=timezone.localtime() - timezone.timedelta(hours=12)):

        if not log_item.fifa_account.is_running_console_trade_one or log_item.save_credit_try >= 33:
            log_item.current_credit = log_item.fifa_account.credit
            yesterday_credit = FifaAccountCreditLog.objects.exclude(
                id=log_item.id,
            ).filter(fifa_account=log_item.fifa_account).last()
            today_production = 0
            if yesterday_credit:
                today_production = (log_item.fifa_account.credit - yesterday_credit.current_credit +
                                    (log_item.today_discharges_coin + log_item.today_discharges_mode2_coin + log_item.today_discharges_mode3_coin))
            log_item.today_production = today_production
        log_item.save_credit_try = log_item.save_credit_try + 1
        log_item.save()


@periodic_task(run_every=crontab(hour="16", minute='30'))
def send_duplicate_accounts():
    bot = telepot.Bot(FIFA_REPORT_TOKEN)
    for console_item in Console.objects.filter(is_active=True):
        duplicates = console_item.fifaaccount_set.filter(
            Q(delete_console_reason=None) | Q(delete_console_reason=''),
        ).values('name_in_console', 'platform').annotate(
            Count('id')).order_by().filter(id__count__gt=1)
        if duplicates:
            text = f"duplicate account in one console : {console_item.name}," \
                   f" {list(duplicates.values('name_in_console', 'platform', 'id__count'))}"
            chat_id = '-885694083'
            bot.sendMessage(chat_id, text)

    # remove proxy from accounts that not work for two month
    FifaAccount.objects.filter(last_run_time__lt=timezone.localtime() - timezone.timedelta(days=2 * 31)).exclude(
        proxy=None).update(proxy=None)

    disabled_in_24 = FifaAccountDisable.objects.filter(
        create_time__gt=timezone.localtime()-timezone.timedelta(hours=24),
        disable_mode=0
    )
    if disabled_in_24:
        text2 = f"{disabled_in_24.count()} account disabled in 24 hour"
        chat_id = '-885694083'
        bot.sendMessage(chat_id, text2)

    not_yet_to_web_transfer_ban_24 = FifaAccountDisable.objects.filter(
        create_time__gt=timezone.localtime()-timezone.timedelta(hours=24),
        disable_mode=3
    )
    if not_yet_to_web_transfer_ban_24:
        text2 = (f"{not_yet_to_web_transfer_ban_24.count()} "
                 f"account change trade access from not yet to web transfer ban in 24 hour")
        chat_id = '-885694083'
        bot.sendMessage(chat_id, text2)

    transfer_ban_24 = FifaAccountDisable.objects.filter(
        create_time__gt=timezone.localtime()-timezone.timedelta(hours=24),
        disable_mode=1
    )
    if transfer_ban_24:
        text2 = f"{transfer_ban_24.count()} account transfer ban in 24 hour"
        chat_id = '-885694083'
        bot.sendMessage(chat_id, text2)
        for disabled_account in transfer_ban_24[:10]:
            last_discharged_24_hour = DischargeMode2.objects.filter(fifa_account=disabled_account.fifa_account).last()
            if last_discharged_24_hour:
                text3 = (f"{disabled_account.fifa_account} account transfer ban\n"
                         f"last discharge card : {last_discharged_24_hour}\n"
                         f"site : {last_discharged_24_hour.mule.site_name}\n"
                         f"player : {last_discharged_24_hour.player_name}")
                bot.sendMessage(chat_id, text3)
            time.sleep(5)



@periodic_task(run_every=crontab(minute='1'))
def save_hourly_log_history():
    from sbc.models import SBCProcess, HourlyAccountSBCDoneHistory

    last_statuses = ConsoleLastStatus.objects.all()
    for status in last_statuses:
        ConsoleLastStatusHistory.objects.create(
            console=status.console,
            fifa_account=status.fifa_account,
            sbc_worker=status.sbc_worker,
            status=status.status,
            status_change_time=status.status_change_time,
            description=status.description,
            description_editor=status.description_editor
        )
    one_hour_ago = timezone.localtime() - timezone.timedelta(hours=1)
    HourlyAccountDischargeHistory.objects.create(
        create_time=one_hour_ago,
        mode2_count=DischargeMode2.objects.filter(status__in=['success', 'failed'], create_time__gt=timezone.localtime() - timezone.timedelta(hours=1)).count(),
        mode3_count=CloseWebAppTransfers.objects.filter(second_side_done=True, create_time__gt=timezone.localtime() - timezone.timedelta(hours=1)).count(),
    )
    HourlyAccountSBCDoneHistory.objects.create(
        create_time=one_hour_ago,
        done_count=SBCProcess.objects.filter(is_done=True, has_error=False, create_time__gt=timezone.localtime() - timezone.timedelta(hours=1)).count(),
        account_done_count=SBCProcess.objects.filter(is_done=True, has_error=False, create_time__gt=timezone.localtime() - timezone.timedelta(hours=1)).distinct('worker__fifa_account').count(),
    )
    HourlyAccountLoginLogHistory.objects.create(
        create_time=one_hour_ago,
        login_with_requests_count=AccountLoginLog.objects.filter(login_method=1, create_time__gt=timezone.localtime() - timezone.timedelta(hours=1)).count(),
        success_login_with_requests_count=AccountLoginLog.objects.filter(login_method=1, login_status=1, create_time__gt=timezone.localtime() - timezone.timedelta(hours=1)).count(),
        login_with_selenium_count=AccountLoginLog.objects.filter(login_method=2, create_time__gt=timezone.localtime() - timezone.timedelta(hours=1)).count(),
        success_login_with_selenium_count=AccountLoginLog.objects.filter(login_method=2, login_status=1, create_time__gt=timezone.localtime() - timezone.timedelta(hours=1)).count(),
        get_new_access_token_count=AccountLoginLog.objects.filter(login_method=3, create_time__gt=timezone.localtime() - timezone.timedelta(hours=1)).count(),
        success_get_new_access_token_count=AccountLoginLog.objects.filter(login_method=3, login_status=1, create_time__gt=timezone.localtime() - timezone.timedelta(hours=1)).count(),
    )

    one_hour_requests = FifaAccountRequest.objects.using('logs_pgbouncer').filter(
            create_time__gte=one_hour_ago,
        ).values('link').annotate(link_count=Count('id')).order_by('-link_count')

    for rrre in one_hour_requests:
        HourlyFifaAccountRequestHistory.objects.create(
            create_time=one_hour_ago,
            link=rrre['link'],
            request_count=rrre['link_count'],
        )


@app.task
def check_services_status():
    from utils.realy_public_methods import telegram_send_message

    errors = []
    # 1. Check all databases
    for alias in connections:
        try:
            connections[alias].cursor()
        except OperationalError as e:
            errors.append(f"❌ Database '{alias}' connection failed: {e}")

    # 2. Check Redis
    try:
        r = get_redis_connection("print_log")
        r.ping()
    except Exception as e:
        errors.append(f"❌ Redis connection failed: {e}")

    # 3. Check Celery workers
    try:
        response = app.control.ping(timeout=2)
        if not response:
            errors.append("❌ No active Celery workers found.")
    except Exception as e:
        errors.append(f"❌ Celery ping failed: {e}")

    # 4. Send Telegram message if errors exist
    if errors:
        message = "🚨 *Service Check Failed:*\n\n" + "\n".join(errors)
        telegram_send_message(
            FIFA_REPORT_TOKEN, message, chat_id=ARZBAZI_REPORT_GROUP_ID,
            message_thread_id=ARZBAZI_REPORT_TOPIC_GENERAL_THREAD_MESSAGE_ID)

    return "All OK" if not errors else "Errors detected"


@app.task
def update_arduino_electric_node_automate():
    local_time = timezone.localtime()
    ports = ArduinoPort.objects.filter(auto_update=True).select_related('electric_node')

    update_list = []
    for port in ports:
        node = port.electric_node
        new_state = 1 if (node and is_node_active(node.start_hour, node.end_hour, local_time)) else 0
        if port.current_state != new_state:
            port.current_state = new_state
            update_list.append(port)

    if update_list:
        ArduinoPort.objects.bulk_update(update_list, ['current_state'])