from datetime import datetime
import pytz

from sqlalchemy import tuple_
from sqlalchemy.sql import and_, func, join, select, update

from maps_adv.adv_store.api.schemas import enums
from maps_adv.adv_store.lib.data_managers.enums import ReasonCampaignStoppedEnum
from maps_adv.adv_store.lib.db import db
from maps_adv.adv_store.v2.lib.db import tables

from .campaign_change_log import (
    CampaignsChangeLogActionName,
    insert_campaign_change_log,
    refresh_campaigns_change_logs,
)


max_timestamps_q = (
    select(
        [
            tables.status_history.c.campaign_id,
            func.max(tables.status_history.c.changed_datetime),
        ]
    )
    .select_from(tables.status_history)
    .group_by(tables.status_history.c.campaign_id)
    .alias("max_timestamps_q")
)


def end_of_day(time: datetime, tz: str):
    localized = time.astimezone(pytz.timezone(tz))
    return localized.replace(hour=23, minute=59, second=59)


async def stop_campaigns_date_end(stop_ending_before: datetime):
    finished_active_campaigns_query = (
        select([tables.campaign.c.id, tables.campaign.c.order_id, tables.campaign.c.timezone, tables.campaign.c.settings])
        .select_from(
            join(
                tables.campaign,
                tables.status_history,
                tables.campaign.c.id == tables.status_history.c.campaign_id,
            )
        )
        .where(
            and_(
                tables.campaign.c.end_datetime < stop_ending_before,
                tuple_(
                    tables.status_history.c.campaign_id,
                    tables.status_history.c.changed_datetime,
                ).in_(max_timestamps_q),
                tables.status_history.c.status.in_(
                    [enums.CampaignStatusEnum.ACTIVE, enums.CampaignStatusEnum.PAUSED]
                ),
            )
        )
    )
    stop_metadata = {
        "due_to": stop_ending_before.timestamp(),
        "reason_stopped": ReasonCampaignStoppedEnum.END_DATETIME.name,
    }

    async with db.rw.transaction():
        finished_active_campaigns = await db.rw.fetch_all(finished_active_campaigns_query)

        campaigns_to_stop = []
        candidate_to_prolongate = []
        for item in finished_active_campaigns:
            if item["settings"].get("auto_prolongation"):
                candidate_to_prolongate.append(dict(item))
            else:
                campaigns_to_stop.append(item["id"])

        change_log_ids = []
        for campaign in campaigns_to_stop:
            change_log_ids.append(
                await insert_campaign_change_log(
                    campaign_id=campaign,
                    action_name=CampaignsChangeLogActionName.CAMPAIGN_STOPPED,
                    **stop_metadata,
                )
            )
        for item in candidate_to_prolongate:
            change_log_ids.append(
                await insert_campaign_change_log(
                    campaign_id=item["id"],
                    action_name=CampaignsChangeLogActionName.CAMPAIGN_PROLONGATED,
                )
            )

        status_entries = []
        for campaign in campaigns_to_stop:
            status_entries.append(
                {
                    "campaign_id": campaign,
                    "author_id": 0,
                    "status": enums.CampaignStatusEnum.DONE,
                    "metadata": stop_metadata,
                }
            )
        if status_entries:
            await db.rw.execute(tables.status_history.insert(status_entries))

        for item in candidate_to_prolongate:
            await db.rw.execute(
                update(tables.campaign)
                .where(tables.campaign.c.id == item["id"])
                .values(end_datetime=end_of_day(stop_ending_before, item["timezone"]))
            )

        if change_log_ids:
            await refresh_campaigns_change_logs(ids=change_log_ids)
