from celery.schedules import crontab
from celery.task import periodic_task
from django.db.models import Q, Subquery, OuterRef, IntegerField, Value, Sum
from django.db.models.functions import Coalesce
from django.utils import timezone

from accounts.models import FifaAccount, ConsoleBotSetting
from futplus.celery_conf import app
from sbc.public_methods import new_print
from trade.models import TradeWorker, TradeLog, SinglePlayerTradeWorker, WebTradeInfo, WebTradeHistory, \
    ConsoleTradeInvestDailyProfitLog, ConsoleTradeOneHistory, ConsoleInvestTradeItemsCountLog
from trade.trade_bot import TraderBot


@app.on_after_finalize.connect
def periodic_tasks(sender, **kwargs):
    print('trade periodic_tasks started')
    # sender.add_periodic_task(5.0, trader_manager.s(), name='active trade manager')
    # sender.add_periodic_task(5.0, single_player_trader_manager.s(), name='active single player trade manager')
    # sender.add_periodic_task(10.0, check_ready_trades.s(), name='check ready trades')
    # sender.add_periodic_task(10.0, check_single_player_trade_again.s(), name='check bid single player trades')
    # sender.add_periodic_task(30.0, web_trade_manager.s(), name='web trade manager')


@app.task
def trader_manager():
    # todo : filter workers after work done . if before worker not done will expire session
    trade_workers = TradeWorker.objects.filter(
        trade_order__status__in=['0', '1'],
        last_run_time__lt=timezone.localtime() - timezone.timedelta(seconds=60 * 75),
        # is_done=False,
        has_error=0,
    )
    trade_worker = trade_workers.first()
    if trade_worker:
        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 + 30 < max_concurrency:
            # if active_concurrency < 80:
            active_trade_bot.delay(trade_worker.id, trade_worker.trade_order.id)
            # print('account %s trade worker started ...' % trade_worker.fifa_account.user_name)


@app.task(bind=True)
def active_trade_bot(self, trade_worker_id, order_id):
    worker = TradeWorker.objects.get(id=trade_worker_id)
    fifa_account = worker.fifa_account
    fifa_account.active = True
    fifa_account.save(update_fields=['active'])
    worker.last_run_time = timezone.localtime()
    worker.task_id = self.request.id
    worker.is_done = 0
    worker.status = 'working'
    worker.save()
    TraderBot(fifa_account=fifa_account, trade_worker=worker).start_trade()
    new_print(fifa_account, 'Trade Complete')
    fifa_account.refresh_from_db()
    fifa_account.active = False
    fifa_account.save(update_fields=['active'])
    worker.refresh_from_db()
    worker.is_done = 1
    worker.status = 'done'
    worker.save(update_fields=['is_done', 'status'])


@app.task(bind=True)
def check_ready_trades(self):
    log_items = TradeLog.objects.filter(
        status=0, expires__lt=timezone.localtime() - timezone.timedelta(seconds=60 * 2)
    ).exclude(
        expires__lt=timezone.localtime() - timezone.timedelta(seconds=60 * 60)
    ).distinct('fifa_account')
    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 + 30 < max_concurrency:
        for log_item in log_items:
            log_item.status = 1
            log_item.save()
            complete_trade.delay(log_item.id)
            # print('want to complete trade :', log_item.id)
            # active_trade_bot.delay(trade_worker.id, trade_worker.trade_order.id)
        # print('account %s trade worker started ...' % trade_worker.fifa_account.user_name)


@app.task
def complete_trade(log_item_id):
    log_item = TradeLog.objects.get(id=log_item_id)
    if log_item.trade_worker:
        TraderBot(fifa_account=log_item.fifa_account, trade_worker=log_item.trade_worker).sell_success_bid(log_item)
    elif log_item.single_player_trade_worker:
        TraderBot(
            fifa_account=log_item.fifa_account, trade_worker=log_item.single_player_trade_worker
        ).sell_success_bid(log_item)
    else:
        new_print(log_item.fifa_account, 'can not find any type of worker')
        return 'can not find any type of worker'


@app.task
def single_player_trader_manager():
    # todo : filter workers after work done . if before worker not done will expire session
    trade_worker = SinglePlayerTradeWorker.objects.filter(
        trade_order__status__in=['0', '1'],
        last_run_time__lt=timezone.localtime() - timezone.timedelta(seconds=60),
        # is_done=False,
        has_error=0,
    ).exclude(status='working').first()
    if trade_worker:
        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 + 30 < max_concurrency:
            # if active_concurrency < 80:
            active_single_player_bot.delay(trade_worker.id, trade_worker.trade_order.id)
            # print('account %s trade worker started ...' % trade_worker.fifa_account.user_name)


@app.task(bind=True)
def active_single_player_bot(self, trade_worker_id, order_id):
    worker = SinglePlayerTradeWorker.objects.get(id=trade_worker_id)
    if len(worker.unused_fifa_accounts.all()) < 1:
        worker.unused_fifa_accounts.set(worker.used_fifa_accounts.all())
        worker.last_run_time = timezone.localtime() + timezone.timedelta(seconds=60 * 60)
        worker.save()
        return 'sleep this worker for one hour'
    fifa_account = worker.unused_fifa_accounts.order_by('last_login_time').first()
    # worker.used_fifa_accounts.add(fifa_account)
    # worker.unused_fifa_accounts.remove(fifa_account)
    worker.last_run_time = timezone.localtime()
    worker.task_id = self.request.id
    worker.is_done = 0
    worker.status = 'working'
    worker.save()
    TraderBot(fifa_account=fifa_account, trade_worker=worker).start_trade_single_player()
    new_print(fifa_account, 'Trade Complete')
    fifa_account.refresh_from_db()
    fifa_account.active = False
    fifa_account.save()
    worker.refresh_from_db()
    worker.is_done = 1
    worker.status = 'done'
    worker.save()


@app.task(bind=True)
def check_single_player_trade_again(self):
    log_items = TradeLog.objects.filter(
        status=0, expires__lt=timezone.localtime() - timezone.timedelta(seconds=30)
    ).exclude(
        expires__lt=timezone.localtime() - timezone.timedelta(seconds=60 * 3)
    )
    # ).distinct('fifa_account')
    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 + 30 < max_concurrency:
        for log_item in log_items[:50]:
            # log_item.status = 1
            # log_item.save()
            check_trade_again.delay(log_item.id)


@app.task
def check_trade_again(log_item_id):
    log_item = TradeLog.objects.get(id=log_item_id)
    TraderBot(
        fifa_account=log_item.fifa_account, trade_worker=log_item.single_player_trade_worker
    ).check_single_player_bid_again(log_item)


@app.task
def web_trade_manager():
    from trade.web_trade_bot import WebTradeRunner
    for web_trade_info in WebTradeInfo.objects.exclude(
            Q(quality_name=None) | Q(quality_name='') | Q(deleted=True)
    ).filter(is_running_trade=False, run_trade=True):
        last_trade_history = web_trade_info.webtradehistory_set.last()
        if not last_trade_history or (
                last_trade_history.create_time < timezone.localtime() - timezone.timedelta(hours=30) and
                last_trade_history.update_time < timezone.localtime() - timezone.timedelta(hours=1)
        ):
            return WebTradeRunner(fifa_account=web_trade_info.fifa_account).web_trade_bot_core()

    for web_trade_info_2 in WebTradeInfo.objects.exclude(deleted=True).filter(is_running_trade=True):
        last_trade_history_2 = web_trade_info_2.webtradehistory_set.last()
        if last_trade_history_2.update_time < timezone.localtime() - timezone.timedelta(hours=1):
            return WebTradeRunner(fifa_account=web_trade_info_2.fifa_account).web_trade_bot_core()
    for web_trade_history in WebTradeHistory.objects.exclude(web_trade_info__deleted=True).filter(
            (Q(quality_name=None) | Q(quality_name='')),
            web_trade_info__is_running_trade=False,
            update_time__lt=timezone.localtime() - timezone.timedelta(hours=1),
            end_time=None
    ):
        last_trade_history_3 = web_trade_history.web_trade_info.webtradehistory_set.last()
        if last_trade_history_3.update_time < timezone.localtime() - timezone.timedelta(hours=1):
            return WebTradeRunner(fifa_account=web_trade_history.web_trade_info.fifa_account).web_trade_bot_core()


@periodic_task(run_every=crontab(hour="23", minute='30'))
def save_console_invest_trade_daily_profit():
    counter = 0
    local_now = timezone.localtime()
    for fifa_acc_item in FifaAccount.objects.exclude(console=None).filter(run_console_invest_trade_one=True):
        counter += 1
        last_trade_history = ConsoleTradeOneHistory.objects.filter(fifa_account=fifa_acc_item).last()
        if last_trade_history:
            forcast = 0
            credit_change = 0
            quality_price = {
                'bronze1': 260,
                'silver0': 260,
                'silver1': 450,
                'gold0': 550,
                'gold1': 940,
                'position_modifier': 1100,
            }
            card_sell_price = quality_price.get(fifa_acc_item.console_trade_one_quality)
            if not card_sell_price:
                continue
            if last_trade_history.unassigned_item_count > 0:
                transfer_list_count = 100
                if last_trade_history.unassigned_item_count >= 50:
                    forcast = (transfer_list_count + 100) * card_sell_price
                else:
                    forcast = (transfer_list_count + last_trade_history.unassigned_item_count) * card_sell_price
            last_profit_log = ConsoleTradeInvestDailyProfitLog.objects.filter(fifa_account=fifa_acc_item).last()

            if last_profit_log:
                credit_change = fifa_acc_item.credit - last_profit_log.current_credit
                forcast_change = forcast - last_profit_log.today_forcast
                if last_profit_log.current_trade_history != last_trade_history:
                    last_sell_count = (last_profit_log.current_trade_history.win_count -
                                       last_profit_log.current_trade_history_win_count) + 100
                    a = last_trade_history.win_count - 100 if last_trade_history.win_count - 100 > 0 else 0
                    today_sell_count = a + last_sell_count
                else:
                    today_sell_count = last_trade_history.win_count - last_profit_log.current_trade_history_win_count
            else:
                forcast_change = forcast
                today_sell_count = last_trade_history.win_count - 100 if last_trade_history.win_count - 100 > 0 else 0
            ConsoleTradeInvestDailyProfitLog.objects.create(
                create_time=local_now,
                fifa_account=fifa_acc_item,
                current_credit=fifa_acc_item.credit,
                today_forcast=forcast,
                current_trade_history=last_trade_history,
                current_trade_history_win_count=last_trade_history.win_count,
                today_sell_count=today_sell_count,
                credit_change=credit_change,
                forcast_change=forcast_change,
                quality_name=fifa_acc_item.console_trade_one_quality,
            )
    return counter


@periodic_task(run_every=crontab(minute='30'))
def save_console_invest_trade_items_count():
    local_now = timezone.localtime()
    objects_list = []
    for item in ConsoleTradeOneHistory.objects.filter(
            Q(bought_items_managed=True) | Q(list_try_count__gte=1),
            update_time__gt=timezone.localtime()-timezone.timedelta(days=3),
    ).order_by('fifa_account', '-update_time').distinct('fifa_account').annotate(
        all_list_try_count=Coalesce(Subquery(ConsoleTradeOneHistory.objects.filter(
                            fifa_account__id=OuterRef('fifa_account__id'),
                        ).values('fifa_account__id').annotate(
                            try_count=Sum('list_try_count')
                        ).values('try_count'), output_field=IntegerField()), Value(0))
    ):
        last_count_log = ConsoleInvestTradeItemsCountLog.objects.filter(fifa_account=item.fifa_account).last()
        added_club_bronze1_items = 0
        added_club_silver0_items = 0
        added_club_silver1_items = 0
        added_club_gold0_items = 0
        added_club_gold1_items = 0
        added_club_position_modifier_items = 0
        added_sold_items = 0
        added_win_items = 0
        complete_trade_count = 0
        complete_trade_count_new = 0
        if last_count_log:
            added_club_bronze1_items = item.club_bronze1_items - last_count_log.club_bronze1_items
            added_club_silver0_items = item.club_silver0_items - last_count_log.club_silver0_items
            added_club_silver1_items = item.club_silver1_items - last_count_log.club_silver1_items
            added_club_gold0_items = item.club_gold0_items - last_count_log.club_gold0_items
            added_club_gold1_items = item.club_gold1_items - last_count_log.club_gold1_items
            added_club_position_modifier_items = item.club_position_modifier_items - last_count_log.club_position_modifier_items
            added_sold_items = item.sold_items_count - last_count_log.sold_items
            added_win_items = item.win_count - last_count_log.win_items
            if added_sold_items < 0:
                added_sold_items = 0
            if added_win_items < 0:
                added_win_items = 0
            complete_trade_count = item.all_list_try_count
            complete_trade_count_new = item.all_list_try_count - last_count_log.complete_trade_count
        objects_list.append(ConsoleInvestTradeItemsCountLog(
            create_time=local_now,
            fifa_account=item.fifa_account,
            club_bronze1_items=item.club_bronze1_items,
            club_silver0_items=item.club_silver0_items,
            club_silver1_items=item.club_silver1_items,
            club_gold0_items=item.club_gold0_items,
            club_gold1_items=item.club_gold1_items,
            club_position_modifier_items=item.club_position_modifier_items,
            added_club_bronze1_items=added_club_bronze1_items,
            added_club_silver0_items=added_club_silver0_items,
            added_club_silver1_items=added_club_silver1_items,
            added_club_gold0_items=added_club_gold0_items,
            added_club_gold1_items=added_club_gold1_items,
            added_club_position_modifier_items=added_club_position_modifier_items,
            sold_items=item.sold_items_count,
            added_sold_items=added_sold_items,
            win_items=item.win_count,
            added_win_items=added_win_items,
            complete_trade_count=complete_trade_count,
            complete_trade_count_new=complete_trade_count_new,
        ))
    ConsoleInvestTradeItemsCountLog.objects.bulk_create(objects_list)
