from __future__ import annotations

"""
APScheduler entrypoints mirroring Laravel cron endpoints.
Wire these in your process supervisor or call from FastAPI lifespan.
"""

import logging

from apscheduler.schedulers.background import BackgroundScheduler
from sqlalchemy import text

from app.config import get_settings
from app.database import session_scope_cluster, session_scope_default
from app.utils.cluster_table_names import campaigns_table
from app.repositories.schema_helper import has_table
from app.services.campaign_core import (
    check_remaining_minutes,
    update_waiting_time_and_retry_for_scheduled_campaign,
)
from app.utils.queue_dispatch import dispatch_campaign_to_queue
from app.utils.timezone_util import format_dt, now_app

logger = logging.getLogger(__name__)


def process_scheduled_campaigns_cron_tick() -> None:
    """
    Simplified port of processScheduledCampaignsCron: pending rows in scheduled_campaigns
    for current minute + dispatch queue. Full cluster-table minute window can be extended.
    """
    now = now_app()
    current_date_time = format_dt(now)
    minute_start = now.replace(second=0, microsecond=0)
    minute_end = now.replace(second=59, microsecond=999999)

    with session_scope_default() as sd, session_scope_cluster() as sc:
        if not has_table(sd, "scheduled_campaigns"):
            return
        businesses = sd.execute(text("SELECT business_id FROM businesses WHERE status = 'active'")).fetchall()
        for (business_id,) in businesses:
            table_name = campaigns_table(business_id)
            if not has_table(sc, table_name):
                continue
            rows = sd.execute(
                text(
                    """
                    SELECT id, campaign_id, business_id FROM scheduled_campaigns
                    WHERE business_id = :bid AND status = 'pending'
                    AND execute_at BETWEEN :ms AND :me
                    """
                ),
                {
                    "bid": business_id,
                    "ms": format_dt(minute_start),
                    "me": format_dt(minute_end),
                },
            ).fetchall()
            for _row_id, campaign_id, bid in rows:
                try:
                    campaign = sc.execute(
                        text(f"SELECT * FROM `{table_name}` WHERE campaign_id = :cid LIMIT 1"),
                        {"cid": campaign_id},
                    ).mappings().first()
                    if not campaign:
                        continue
                    mc = check_remaining_minutes(sd, int(bid))
                    if not mc["valid"]:
                        sd.execute(
                            text(
                                "UPDATE scheduled_campaigns SET status = 'failed', error_message = :m, updated_at = :u WHERE id = :id"
                            ),
                            {"m": mc["message"], "u": current_date_time, "id": _row_id},
                        )
                        sc.execute(
                            text(f"UPDATE `{table_name}` SET status = 'paused', updated_at = :u WHERE campaign_id = :cid"),
                            {"u": now_app(), "cid": campaign_id},
                        )
                        continue
                    sc.execute(
                        text(f"UPDATE `{table_name}` SET status = 'active', updated_at = :u WHERE campaign_id = :cid"),
                        {"u": now_app(), "cid": campaign_id},
                    )
                    update_waiting_time_and_retry_for_scheduled_campaign(sc, sd, int(bid), int(campaign_id))
                    sd.execute(
                        text("UPDATE scheduled_campaigns SET status = 'completed', updated_at = :u WHERE id = :id"),
                        {"u": current_date_time, "id": _row_id},
                    )
                    dispatch_campaign_to_queue(sd, int(campaign_id), int(bid), False, False)
                except Exception as e:  # noqa: BLE001
                    logger.error("scheduled row error: %s", e)


def start_scheduler() -> BackgroundScheduler:
    sched = BackgroundScheduler(timezone=get_settings().timezone)
    sched.add_job(process_scheduled_campaigns_cron_tick, "interval", minutes=1, id="scheduled_campaigns")
    sched.start()
    return sched
