"""Auto pause/resume per-BID orchestration when usage allocation is exhausted."""

from __future__ import annotations

import logging
from typing import Any, Callable, Dict, List, Optional

from usage_allocation_util import evaluate_usage_allocation
from usage_validity_alert_service import maybe_send_usage_validity_alert

logger = logging.getLogger(__name__)

StopLoopFn = Optional[Callable[[str], bool]]


def _default_stop_loop(bid: str) -> bool:
    try:
        from orchestrator_supervisor import stop_loop

        return stop_loop(bid)
    except Exception as exc:
        logger.warning("[%s] Failed to stop orchestrator loop: %s", bid, exc)
        return False


def pause_pipeline_if_usage_exhausted(
    db_handler,
    bid: str,
    *,
    stop_loop_fn: StopLoopFn = None,
) -> bool:
    """
    Disable pipeline_enabled and stop the orchestrator loop when usage is blocked.
    Returns True if the pipeline was paused by this call.
    """
    bid = str(bid).strip()
    if not bid:
        return False

    db_handler.ensure_business_pipeline_config_table()
    cfg = db_handler.get_pipeline_config(bid) or {}
    if not int(cfg.get("pipeline_enabled") or 0):
        return False

    with db_handler.get_connection() as conn:
        with conn.cursor() as cursor:
            usage = evaluate_usage_allocation(cursor, bid)
            if not usage.get("blocked"):
                return False
            # Send while the connection is open so sent_state can persist.
            # Instant pause used to skip email and only rely on the supervisor sweep.
            maybe_send_usage_validity_alert(cursor, bid, usage)

    logger.warning(
        "Usage limit exhausted for BID %s (%s/%s min); auto-stopping pipeline",
        bid,
        usage["used_minutes"],
        usage["monthly_minute_limit"],
    )
    db_handler.save_pipeline_config(
        bid,
        {
            "pipeline_enabled": 0,
            "usage_limit_paused": 1,
        },
    )
    stop_fn = stop_loop_fn or _default_stop_loop
    stop_fn(bid)
    return True


def resume_pipeline_if_usage_available(
    db_handler,
    bid: str,
    *,
    start_loop_fn: StopLoopFn = None,
) -> bool:
    """
    Re-enable pipeline when it was auto-paused for usage and headroom is available again.
    Returns True if the pipeline was resumed by this call.
    """
    bid = str(bid).strip()
    if not bid:
        return False

    db_handler.ensure_business_pipeline_config_table()
    cfg = db_handler.get_pipeline_config(bid) or {}
    if not int(cfg.get("usage_limit_paused") or 0):
        return False
    if int(cfg.get("pipeline_enabled") or 0):
        db_handler.save_pipeline_config(bid, {"usage_limit_paused": 0})
        return False

    with db_handler.get_connection() as conn:
        with conn.cursor() as cursor:
            usage = evaluate_usage_allocation(cursor, bid)
            if usage.get("blocked"):
                return False

    logger.info(
        "Usage headroom restored for BID %s (%s/%s min); auto-resuming pipeline",
        bid,
        usage["used_minutes"],
        usage["monthly_minute_limit"],
    )
    db_handler.save_pipeline_config(
        bid,
        {
            "pipeline_enabled": 1,
            "usage_limit_paused": 0,
        },
    )
    if start_loop_fn:
        start_loop_fn(bid)
    return True


def sync_usage_limit_pipeline_states(
    db_handler,
    *,
    stop_loop_fn: StopLoopFn = None,
    start_loop_fn: StopLoopFn = None,
) -> Dict[str, Any]:
    """Supervisor hook: pause exhausted BIDs; resume auto-paused BIDs with headroom."""
    db_handler.ensure_business_pipeline_config_table()
    paused: List[str] = []
    resumed: List[str] = []

    with db_handler.get_connection() as conn:
        cursor = conn.cursor()
        cursor.execute(
            """
            SELECT bid, pipeline_enabled, usage_limit_paused
            FROM business_pipeline_config
            """
        )
        rows = cursor.fetchall() or []

    stop_fn = stop_loop_fn or _default_stop_loop
    for row in rows:
        bid = str(row.get("bid") or "").strip()
        if not bid:
            continue
        enabled = int(row.get("pipeline_enabled") or 0)
        usage_paused = int(row.get("usage_limit_paused") or 0)

        with db_handler.get_connection() as conn:
            with conn.cursor() as cursor:
                usage = evaluate_usage_allocation(cursor, bid)
                maybe_send_usage_validity_alert(cursor, bid, usage)

        if usage.get("blocked") and enabled:
            db_handler.save_pipeline_config(
                bid,
                {"pipeline_enabled": 0, "usage_limit_paused": 1},
            )
            stop_fn(bid)
            paused.append(bid)
            logger.warning(
                "Supervisor paused BID %s pipeline (usage %s/%s min)",
                bid,
                usage["used_minutes"],
                usage["monthly_minute_limit"],
            )
        elif usage_paused and not usage.get("blocked"):
            db_handler.save_pipeline_config(
                bid,
                {"pipeline_enabled": 1, "usage_limit_paused": 0},
            )
            if start_loop_fn:
                start_loop_fn(bid)
            resumed.append(bid)
            logger.info(
                "Supervisor resumed BID %s pipeline (usage %s/%s min)",
                bid,
                usage["used_minutes"],
                usage["monthly_minute_limit"],
            )

    return {"paused": paused, "resumed": resumed}
