"""
Периодические таски запускаются сразу на нескольких инстансах.
При этом в arq по-умолчанию работает уникальность задач.
НО если первая таска отработает очень быстро, то может быть ситуация,
когда другая таска поставится уже после того, как завершится первая.
Поэтому делаем тут искусственный sleep.
"""
import asyncio
import logging
import pytz

from datetime import datetime, timedelta

from intranet.trip.lib.arq_utils.decorators import periodic_job

from intranet.trip.src.cache import Cache
from intranet.trip.src.config import settings
from intranet.trip.src.enums import PTStatus, Provider
from intranet.trip.src.lib.hub.sync import sync_hub_profiles
from intranet.trip.src.lib.messenger.sync import MessengerSync
from intranet.trip.src.lib.sftp_client import registry_batch_upload
from intranet.trip.src.lib.staff.sync import StaffTripPull, StaffTripPush
from intranet.trip.src.lib.staffapi.sync import (
    StaffApiProfileSync,
    StaffApiRolesSync,
    StaffApiOrganizationSync,
)
from intranet.trip.src.logic.aeroclub.trips import AeroclubTripController
from intranet.trip.src.unit_of_work import UnitOfWork


logger = logging.getLogger(__name__)


@periodic_job(
    predicate=settings.IS_YA_TEAM and settings.ENABLE_STAFF_SYNC,
)
async def create_staff_trips_task(ctx) -> None:
    logger.info('Start create_staff_trips_task')
    async with ctx['db'].acquire() as conn:
        uow = UnitOfWork(conn=conn, redis=ctx['redis'])
        staff_sync = await StaffTripPush.init(uow)
        await staff_sync.create_staff_trips()
    await asyncio.sleep(1)


@periodic_job(
    predicate=settings.IS_YA_TEAM and settings.ENABLE_STAFF_SYNC,
)
async def pull_staff_trips_task(ctx) -> None:
    logger.info('Start pull_staff_trips_task')
    async with ctx['db'].acquire() as conn:
        uow = UnitOfWork(conn=conn, redis=ctx['redis'])
        staff_sync = await StaffTripPull.init(uow)
        await staff_sync.pull_staff_trips()
    await asyncio.sleep(1)


@periodic_job(
    predicate=settings.IS_YA_TEAM and settings.ENABLE_STAFF_SYNC,
    minute={0, 30},
)
async def sync_profiles_with_staff_task(ctx) -> None:
    logger.info('Start sync_profiles_with_staff')
    async with ctx['db'].acquire() as conn:
        uow = UnitOfWork(conn=conn, redis=ctx['redis'])
        profiles_sync = await StaffApiProfileSync.init(uow)
        roles_sync = await StaffApiRolesSync.init(uow)

        chief_records = await profiles_sync.sync()
        await roles_sync.sync(chief_records)
    await asyncio.sleep(1)


@periodic_job(
    predicate=settings.IS_YA_TEAM and settings.ENABLE_STAFF_SYNC,
    hour=2,
    minute=0,
)
async def sync_organizations_with_staff_task(ctx) -> None:
    logger.info('Start sync_organizations_with_staff')
    async with ctx['db'].acquire() as conn:
        uow = UnitOfWork(conn=conn, redis=ctx['redis'])
        organization_sync = await StaffApiOrganizationSync.init(uow)
        await organization_sync.sync()
    await asyncio.sleep(1)


@periodic_job(
    predicate=settings.ENABLE_AEROCLUB_SYNC,
    minute=set(range(0, 60, 5)),
)
async def create_missing_aeroclub_trips_task(ctx):
    logger.info('Start create_missing_aeroclub_trips task')
    async with ctx['db'].acquire() as conn:
        uow = UnitOfWork(conn=conn, redis=ctx['redis'])
        aeroclub_trip_ctl = AeroclubTripController(uow)
        await aeroclub_trip_ctl.create_aeroclub_trips()
    await asyncio.sleep(1)


@periodic_job(
    predicate=settings.ENABLE_AEROCLUB_SYNC,
    minute=set(range(2, 60, 5)),
)
async def authorize_aeroclub_trips_task(ctx, trip_id: int = None, person_id: int = None):
    logger.info('Start authorize_aeroclub_trips task')
    async with ctx['db'].acquire() as conn:
        uow = UnitOfWork(conn=conn, redis=ctx['redis'])
        aeroclub_trip_ctl = await AeroclubTripController.init(uow=uow)
        await aeroclub_trip_ctl.authorize_aeroclub_trips(trip_id=trip_id, person_id=person_id)
    await asyncio.sleep(1)


@periodic_job(
    predicate=settings.ENABLE_AEROCLUB_SYNC,
    hour=1,
    minute=0,
)
async def sync_profiles_to_ihub_task(ctx, person_ids=None, ext_person_ids=None) -> None:
    logger.info('Start sync_profiles_to_ihub task')
    async with ctx['db'].acquire() as conn:
        uow = UnitOfWork(conn=conn, redis=ctx['redis'])
        result = await sync_hub_profiles(uow, person_ids, ext_person_ids)
    logger.info('Finish sync_profiles_to_ihub. %s profiles synced', len(result))
    await asyncio.sleep(1)


async def _run_executor_for_person_trips(ctx, status: PTStatus) -> None:
    async with ctx['db'].acquire() as conn:
        uow = UnitOfWork(conn=conn, redis=ctx['redis'])
        ids = await uow.person_trips.get_person_trip_ids(
            status=status,
            provider=Provider.aeroclub,
        )
        logger.info('%d person trips to execute', len(ids))

        for trip_id, person_id in ids:
            await ctx['redis'].enqueue_job(
                'execute_person_trip_services',
                trip_id=trip_id,
                person_id=person_id,
            )


@periodic_job(
    predicate=settings.ENABLE_AEROCLUB_SYNC,
    minute=set(range(0, 60, settings.ARQ_SERVICES_EXECUTION_RUN_INTERVAL)),
    timeout=settings.ARQ_SERVICES_EXECUTION_JOB_TIMEOUT,
)
async def run_executor_for_executing_person_trips_task(ctx) -> None:
    logger.info('Start run_executor_for_executing_person_trips_task')
    await _run_executor_for_person_trips(ctx, status=PTStatus.executing)
    await asyncio.sleep(1)


@periodic_job(
    predicate=settings.ENABLE_AEROCLUB_SYNC and settings.ENABLE_EXECUTED_PERSON_TRIPS_SYNC,
    minute=10,
    timeout=settings.ARQ_SERVICES_EXECUTION_JOB_TIMEOUT,
)
async def run_executor_for_executed_person_trips_task(ctx) -> None:
    logger.info('Start run_executor_for_executed_person_trips_task')
    await _run_executor_for_person_trips(ctx, status=PTStatus.executed)
    await asyncio.sleep(1)


@periodic_job(
    predicate=settings.ENABLE_CHATS,
    minute=set(range(1, 60, 5)),
)
async def create_chats_task(ctx) -> None:
    logger.info('Start create_chats_task')
    async with ctx['db'].acquire() as conn:
        uow = UnitOfWork(conn=conn, redis=ctx['redis'])
        cache = Cache(ctx['redis'])
        chat_sync = await MessengerSync.init(uow, cache)
        await chat_sync.create_or_update_chats()
    await asyncio.sleep(1)


@periodic_job(
    minute=0,
    hour=5,
)
async def close_person_trips_task(ctx) -> None:
    logger.info('Start close_person_trips_task')
    async with ctx['db'].acquire() as conn:
        uow = UnitOfWork(conn=conn, redis=ctx['redis'])
        updated = await uow.person_trips.close_completed_person_trips()
        logger.info('Closed %d person trips', len(updated))
    await asyncio.sleep(1)


@periodic_job(
    minute=10,
    hour=5,
)
async def close_trips_task(ctx) -> None:
    logger.info('Start close_trips_task')
    async with ctx['db'].acquire() as conn:
        uow = UnitOfWork(conn=conn, redis=ctx['redis'])
        updated = await uow.trips.close_completed_trips()
        logger.info('Closed %d trips', len(updated))
    await asyncio.sleep(1)


@periodic_job(
    hour=0,
    minute=30,
)
async def run_active_trips_triggers(ctx) -> None:
    async with ctx['db'].acquire() as conn:
        uow = UnitOfWork(conn=conn, redis=ctx['redis'])
        ids = await uow.person_trips.get_person_trip_ids(status=PTStatus.executed)
        for trip_id, person_id in ids:
            await ctx['redis'].enqueue_job(
                'run_person_trip_triggers',
                trip_id=trip_id,
                person_id=person_id,
            )
    await asyncio.sleep(1)


@periodic_job(
    predicate=settings.YT_ENABLE_SYNC,
    hour=0,
    minute=15,
)
async def run_registry_upload_task(ctx) -> None:
    logger.info('Start run_registry_upload_task')
    yesterday = datetime.now(tz=pytz.timezone('Europe/Moscow')).date() - timedelta(days=1)
    await registry_batch_upload(
        date=yesterday.strftime('%Y-%m-%d'),
        host=settings.AEROCLUB_REGISTRY_HOST,
        login=settings.AEROCLUB_REGISTRY_LOGIN,
        password=settings.AEROCLUB_REGISTRY_PASSWORD,
        yt_prefix=settings.YT_PREFIX,
    )
    await asyncio.sleep(1)
