"""
Calendar / date histogram endpoints.

Used by the UI to mark days that have assets (per month).
"""
import datetime
from pathlib import Path

from aiohttp import web
from mjr_am_backend.adapters.comfy_core import get_input_directory
from mjr_am_backend.config import get_runtime_output_root
from mjr_am_backend.custom_roots import resolve_custom_root
from mjr_am_backend.shared import Result, sanitize_error_message

from ..core import _json_response, _require_services
from ..search.query_sanitizer import parse_request_filters


def _month_bounds(month_value: str) -> Result[tuple[int, int]]:
    """
    Convert YYYY-MM to [start_ts, end_ts) in UTC.
    """
    try:
        dt = datetime.datetime.strptime(month_value, "%Y-%m")
    except Exception:
        return Result.Err("INVALID_INPUT", "Invalid month (expected YYYY-MM)")

    start = dt.replace(day=1, hour=0, minute=0, second=0, microsecond=0, tzinfo=datetime.timezone.utc)
    # First day of next month.
    next_month = (start.replace(day=28) + datetime.timedelta(days=4)).replace(day=1)
    return Result.Ok((int(start.timestamp()), int(next_month.timestamp())))


def register_calendar_routes(routes: web.RouteTableDef) -> None:
    """Register calendar/date-histogram routes."""
    @routes.get("/mjr/am/date-histogram")
    async def date_histogram(request: web.Request):
        """
        Return day->count mapping for a given month.

        Query params:
          scope: output|input|all|custom (default: output)
          month: YYYY-MM (required)
          custom_root_id/root_id: required when scope=custom
          kind: image|video|audio|model3d (optional)
          min_rating: 0..5 (optional)
          has_workflow: true/false (optional)
          min_size_mb/max_size_mb (optional)
          min_width/min_height/max_width/max_height (optional)
          workflow_type (optional)
          date_range/date_exact (optional)
        """
        month = (request.query.get("month") or "").strip()
        if not month:
            return _json_response(Result.Err("INVALID_INPUT", "Missing month (YYYY-MM)"))

        bounds = _month_bounds(month)
        if not bounds.ok:
            return _json_response(bounds)
        month_data = bounds.data
        if not month_data:
            return _json_response(Result.Err("INVALID_INPUT", "Invalid month bounds"))
        month_start, month_end = month_data

        scope = (request.query.get("scope") or "output").strip().lower()

        filters_res = parse_request_filters(request.query)
        if not filters_res.ok:
            return _json_response(filters_res)
        filters = filters_res.data or {}
        if scope in {"output", "input", "custom"}:
            subfolder_param = str(request.query.get("subfolder") or "").strip()
            # Only apply subfolder filter when an explicit non-empty value is provided,
            # matching the behavior of the list query (search_impl.py).
            if subfolder_param:
                filters["subfolder"] = subfolder_param

        svc, error_result = await _require_services()
        if error_result:
            return _json_response(error_result)
        if not isinstance(svc, dict):
            return _json_response(Result.Err("SERVICE_UNAVAILABLE", "Index service unavailable"))

        output_root = str(Path(get_runtime_output_root()).resolve(strict=False))
        input_dir = get_input_directory() or str((Path(__file__).resolve().parents[3] / "input").resolve())
        input_root = str(Path(input_dir).resolve(strict=False))

        roots: list[str] = []

        if scope == "input":
            roots = [input_root]
        elif scope == "all":
            roots = [output_root, input_root]
        elif scope == "custom":
            root_id = request.query.get("custom_root_id", "") or request.query.get("root_id", "")
            root_result = resolve_custom_root(str(root_id or ""))
            if not root_result.ok:
                return _json_response(root_result)
            if not root_result.data:
                return _json_response(Result.Err("NOT_FOUND", "Custom root not found"))
            roots = [str(Path(root_result.data).resolve(strict=False))]
        else:
            # Default: output
            roots = [output_root]

        try:
            res = await svc["index"].date_histogram_scoped(
                roots,
                month_start,
                month_end,
                filters=filters or None,
            )
        except Exception as exc:
            return _json_response(
                Result.Err("DB_ERROR", sanitize_error_message(exc, "Histogram failed"))
            )

        if not res.ok:
            return _json_response(res)

        return _json_response(Result.Ok({"month": month, "days": res.data or {}}))
