"""
Batch Efficiency Analytics Endpoint
=====================================
POST /api/v1/batch/efficiency

Analyses a delivery batch for idle-rider substitution opportunities.
Supports named batch windows (morning / afternoon / evening) or custom
time ranges so you can run the same analysis for any shift.

Request body:
  {
    "batch":     "morning",       // "morning" | "afternoon" | "evening" | "custom"
    "date":      "2026-05-28",    // defaults to today
    "tenant_id": 916,             // defaults to 916
    "from_time": "06:00",         // only for batch="custom"
    "to_time":   "09:00",         // only for batch="custom"
    "deliveries": [...],          // supply inline instead of DB fetch
    "rider_names": {"1036": "Vignesh S", ...},
    "config": {
      "idle_threshold_minutes": 30,
      "road_kmh": 13.0,
      "max_transfer_orders": 4
    }
  }

Batch windows (assigntime range, inclusive start / exclusive end):
  morning   : 06:00 – 09:00   (breakfast + early lunch prep)
  afternoon : 11:00 – 15:00   (lunch)
  evening   : 17:00 – 21:30   (dinner)
  custom    : caller provides from_time / to_time

Header shorthand (all equivalent to body.batch):
  X-Batch-Window: morning | afternoon | evening
"""

import logging
import os
from datetime import date as _date
from typing import Any

from fastapi import APIRouter, Body, Header, HTTPException, status

from app.services.routing.batch_efficiency import analyse_batch

logger = logging.getLogger(__name__)

router = APIRouter(
    prefix="/api/v1/batch",
    tags=["Batch Analytics"],
    responses={500: {"description": "Internal server error"}},
)

# ---------------------------------------------------------------------------
# Named batch windows  {name: (from_time, to_time)}  — 24-h "HH:MM" strings
# ---------------------------------------------------------------------------
BATCH_WINDOWS: dict[str, tuple[str, str]] = {
    "morning":   ("06:00", "09:00"),
    "afternoon": ("11:00", "15:00"),
    "evening":   ("17:00", "21:30"),
}

DEFAULT_BATCH = "morning"


# ---------------------------------------------------------------------------
# DB fetch
# ---------------------------------------------------------------------------

def _fetch_from_db(
    target_date: str,
    tenant_id: int,
    from_time: str,
    to_time: str,
) -> tuple[list[dict], dict[int, str]]:
    """
    Fetch deliveries assigned within [from_time, to_time) on target_date.
    Times are 24-h "HH:MM" strings, e.g. "06:00", "09:00".
    Returns (deliveries, rider_names) where rider_names maps userid → username.
    """
    # Shared nearledb connector (single source of truth for DB_* creds).
    from app.services.routing.delivery_history_service import connect_nearledb

    try:
        conn = connect_nearledb()
    except ImportError:
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail="psycopg2 not installed — cannot fetch from DB.",
        )
    except Exception as exc:
        raise HTTPException(
            status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
            detail=f"DB connection failed: {exc}",
        )

    try:
        cur = conn.cursor()
        cur.execute(
            """
            SELECT
                d.deliveryid,
                d.userid,
                d.pickupcustomer,
                d.assigntime,
                d.arrivaltime,
                d.pickuptime,
                d.deliverytime,
                COALESCE(d.droplat,  d.deliverylat)  AS dlat,
                COALESCE(d.droplon, d.deliverylong)  AS dlon
            FROM deliveries d
            WHERE d.tenantid = %s
              AND DATE(d.assigntime::timestamp) = %s
              AND CAST(SPLIT_PART(d.assigntime, ' ', 2) AS TIME) >= %s
              AND CAST(SPLIT_PART(d.assigntime, ' ', 2) AS TIME) <  %s
              AND COALESCE(d.droplat, d.deliverylat) IS NOT NULL
              AND d.userid IS NOT NULL
            ORDER BY d.userid, d.assigntime
            """,
            (tenant_id, target_date, from_time + ":00", to_time + ":00"),
        )
        cols = [c.name for c in cur.description]
        rows = cur.fetchall()
        cur.close()
        deliveries = [dict(zip(cols, r)) for r in rows]

        # Fetch rider names for the returned userids
        rider_names_db: dict[int, str] = {}
        try:
            unique_uids = list({int(r["userid"]) for r in deliveries if r.get("userid") is not None})
            if unique_uids:
                cur2 = conn.cursor()
                cur2.execute(
                    "SELECT userid, username FROM users WHERE userid = ANY(%s)",
                    (unique_uids,)
                )
                for uid, uname in cur2.fetchall():
                    if uname:
                        rider_names_db[int(uid)] = str(uname)
                cur2.close()
        except Exception:
            pass  # names are non-critical; callers fall back to "Rider {uid}"

        return deliveries, rider_names_db
    except Exception as exc:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"DB query failed: {exc}",
        )
    finally:
        conn.close()


# ---------------------------------------------------------------------------
# Endpoint
# ---------------------------------------------------------------------------

@router.post(
    "/efficiency",
    summary="Batch efficiency analysis",
    description=(
        "Analyse a delivery batch for idle-rider substitution opportunities. "
        "Pass `batch` as 'morning', 'afternoon', or 'evening' to select the "
        "time window automatically, or use `batch='custom'` with `from_time`/`to_time`. "
        "Supply `deliveries` inline to skip the DB fetch entirely."
    ),
)
async def batch_efficiency(
    body: Any = Body(default=None),
    x_batch_window: str | None = Header(default=None),
):
    if body is None:
        body = {}

    # ---- Parse inputs -------------------------------------------------------
    deliveries: list[dict] | None = None
    target_date: str | None = None
    tenant_id: int = 916
    rider_names: dict[int, str] = {}
    cfg: dict = {}
    batch_name: str = DEFAULT_BATCH
    from_time: str | None = None
    to_time: str | None = None

    if isinstance(body, dict):
        deliveries  = body.get("deliveries")
        target_date = body.get("date")
        tenant_id   = int(body.get("tenant_id", 916))
        batch_name  = (body.get("batch") or x_batch_window or DEFAULT_BATCH).lower()
        from_time   = body.get("from_time")
        to_time     = body.get("to_time")
        rider_names_raw = body.get("rider_names") or {}
        rider_names = {int(k): v for k, v in rider_names_raw.items()}
        cfg = body.get("config") or {}
    elif isinstance(body, list):
        deliveries = body
        batch_name = (x_batch_window or DEFAULT_BATCH).lower()

    # ---- Resolve time window ------------------------------------------------
    if batch_name == "custom":
        if not from_time or not to_time:
            raise HTTPException(
                status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
                detail="batch='custom' requires from_time and to_time (e.g. '11:00', '15:00').",
            )
    elif batch_name in BATCH_WINDOWS:
        from_time, to_time = BATCH_WINDOWS[batch_name]
    else:
        raise HTTPException(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            detail=(
                f"Unknown batch '{batch_name}'. "
                f"Valid values: {list(BATCH_WINDOWS.keys())} or 'custom'."
            ),
        )

    # ---- Fetch from DB if no inline deliveries ------------------------------
    if not deliveries:
        if not target_date:
            target_date = str(_date.today())

        logger.info(
            f"[BatchEfficiency] Fetching batch={batch_name} "
            f"date={target_date} window={from_time}-{to_time} tenant={tenant_id}"
        )
        deliveries, db_rider_names = _fetch_from_db(target_date, tenant_id, from_time, to_time)

        if not deliveries:
            return {
                "batch": batch_name,
                "window": {"from": from_time, "to": to_time},
                "date": target_date,
                "fleet_summary": {},
                "rider_timelines": [],
                "substitution_opportunities": [],
                "top_recommendation": None,
                "message": (
                    f"No {batch_name}-batch orders found for {target_date} "
                    f"between {from_time} and {to_time}."
                ),
            }

        # Merge: DB-fetched names as base, request-provided names take precedence
        rider_names = {**db_rider_names, **rider_names}

    logger.info(
        f"[BatchEfficiency] Analysing {len(deliveries)} deliveries — "
        f"batch={batch_name} date={target_date or 'inline'}"
    )

    # ---- Run analysis -------------------------------------------------------
    result = analyse_batch(
        deliveries=deliveries,
        rider_names=rider_names,
        road_kmh=float(cfg.get("road_kmh", 13.0)),
        idle_threshold_min=float(cfg.get("idle_threshold_minutes", 30.0)),
        max_transfer=int(cfg.get("max_transfer_orders", 4)),
    )

    result["batch"]  = batch_name
    result["window"] = {"from": from_time, "to": to_time}
    result["date"]   = target_date or "inline"
    result["input_delivery_count"] = len(deliveries)
    return result
