"""
Lists tasks for Bedrock retry: queries the task table only (no product_images).
Incomplete GenAI for tasks fixed today (SAST). Each item is { task_id } (Map → BedrockImageAnalysis).
Requires non-empty task.image_url; tasks without a shelf URL are skipped.
"""
import logging
import os
from datetime import date, datetime, timedelta
from typing import Any, Dict, List, Optional
from zoneinfo import ZoneInfo

import boto3
from supabase import Client, create_client

logger = logging.getLogger()
logger.setLevel(logging.INFO)

ssm = boto3.client("ssm")

# Calendar day for fixed_at filter (Africa/Johannesburg)
TZ = ZoneInfo("Africa/Johannesburg")
# Page size for Step Functions (keeps state input small; Map output discarded separately)
DEFAULT_BATCH_SIZE = 500


def get_parameter(name: str) -> str:
    r = ssm.get_parameter(Name=name, WithDecryption=True)
    return r["Parameter"]["Value"]


def get_supabase_client() -> Client:
    url = get_parameter("/supabase/url")
    key = get_parameter("/supabase/anon")
    return create_client(url, key)


def _sast_day_bounds_utc(for_day: Optional[date] = None) -> tuple:
    """Return (start_utc_iso, end_utc_iso, run_date_label) for that SAST calendar day."""
    z = TZ
    d = datetime.now(z).date() if for_day is None else for_day
    day_start_local = datetime.combine(d, datetime.min.time(), tzinfo=z)
    day_end_local = day_start_local + timedelta(days=1)
    utc = ZoneInfo("UTC")
    start_utc = day_start_local.astimezone(utc)
    end_utc = day_end_local.astimezone(utc)
    return (
        start_utc.isoformat().replace("+00:00", "Z"),
        end_utc.isoformat().replace("+00:00", "Z"),
        d.isoformat(),
    )


def _task_incomplete(row: Dict[str, Any]) -> bool:
    v = row.get("processed_genai")
    return v is not True


def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
    """
    Returns one page: { items, has_more, next_offset, run_date, timezone, count, total_count }.
    Optional event: run_date "YYYY-MM-DD"; offset (int, default 0) for pagination.
    Batch size: RETRY_LIST_BATCH_SIZE or legacy RETRY_LIST_MAX_ITEMS (default 500).
    """
    batch = int(
        os.environ.get("RETRY_LIST_BATCH_SIZE")
        or os.environ.get("RETRY_LIST_MAX_ITEMS")
        or DEFAULT_BATCH_SIZE
    )
    offset = 0
    if isinstance(event, dict) and event.get("offset") is not None:
        try:
            offset = max(0, int(event["offset"]))
        except (TypeError, ValueError):
            offset = 0
    run_date_str = event.get("run_date") if isinstance(event, dict) else None
    # Later pages must use the same SAST calendar day as page 0
    if offset > 0 and not run_date_str:
        raise ValueError("run_date is required when offset > 0 (paginated retry batch)")
    for_day: Optional[date] = None
    if run_date_str:
        try:
            y, m, d = run_date_str.split("-")
            for_day = date(int(y), int(m), int(d))
        except (ValueError, AttributeError):
            logger.warning("Invalid run_date %s; using today in SAST", run_date_str)
            for_day = None

    start_iso, end_iso, run_date_label = _sast_day_bounds_utc(for_day)
    logger.info("SAST window for fixed_at: %s <= fixed_at < %s (%s)", start_iso, end_iso, run_date_label)

    supabase = get_supabase_client()

    # task table only — all fields used for this list live on task
    tr = (
        supabase.table("task")
        .select("id,image_url,processed_genai,fixed_at")
        .not_.is_("fixed_at", "null")
        .gte("fixed_at", start_iso)
        .lt("fixed_at", end_iso)
        .execute()
    )
    tasks = tr.data or []
    tasks_incomplete = [t for t in tasks if _task_incomplete(t)]

    # Bedrock task_id path uses task.image_url only — one Map item per task.
    skipped_no_image_url = 0
    tasks_runnable: List[Dict[str, Any]] = []
    for t in tasks_incomplete:
        if (t.get("image_url") or "").strip():
            tasks_runnable.append(t)
        else:
            skipped_no_image_url += 1
    if skipped_no_image_url:
        logger.info(
            "ListBedrockRetryTasks: skipped %s incomplete tasks with empty task.image_url (retry emits task_id only)",
            skipped_no_image_url,
        )

    task_ids = [str(t["id"]) for t in tasks_runnable]
    all_items: List[Dict[str, str]] = [{"task_id": tid} for tid in task_ids]

    if not task_ids:
        return {
            "items": [],
            "has_more": False,
            "next_offset": 0,
            "run_date": run_date_label,
            "timezone": "Africa/Johannesburg",
            "count": 0,
            "total_count": 0,
        }

    total_count = len(all_items)
    end = offset + batch
    page = all_items[offset:end]
    next_offset = offset + len(page)
    has_more = next_offset < total_count

    out = {
        "items": page,
        "has_more": has_more,
        "next_offset": next_offset,
        "run_date": run_date_label,
        "timezone": "Africa/Johannesburg",
        "count": len(page),
        "total_count": total_count,
    }
    logger.info(
        "ListBedrockRetryTasks: offset=%s batch=%s total=%s page=%s has_more=%s",
        offset,
        batch,
        total_count,
        len(page),
        has_more,
    )
    return out
