"""list/format/show."""
from __future__ import annotations

import re as _re
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import List, Optional

from .paths import resolve_under_root
from .reconcile import _parse_iso
from .run_index_row import read_run_index


def _parse_since(value: str) -> Optional[datetime]:
    """--since 파싱. 빈 문자열은 None(필터 없음). 형식 위반은 ValueError 로
    fail-fast 한다. 이전엔 잘못된 값(`7days`)이 silently None 으로 강등되어
    필터가 무력화됐고, rerun --filter --since <typo> 가 max-spawn 까지 모든
    historical run 을 spawn 하던 회귀가 있었다.
    """
    if not value:
        return None
    m = _re.match(r"^(\d+)([dhm])$", value)
    if m:
        n, unit = int(m.group(1)), m.group(2)
        delta = {"d": timedelta(days=n), "h": timedelta(hours=n),
                 "m": timedelta(minutes=n)}[unit]
        return datetime.now(timezone.utc).replace(tzinfo=None) - delta
    try:
        return datetime.strptime(value, "%Y-%m-%d")
    except ValueError as exc:
        raise ValueError(
            f"invalid --since {value!r}: expected <N>[dhm] or YYYY-MM-DD"
        ) from exc


def _parse_started_at(ts: str) -> Optional[datetime]:
    """--since 비교용 startedAt 파싱. 기준값은 naive UTC 이므로 결과도 naive UTC.

    _parse_iso 는 `...Z` 형식만 받지만 backfill 이 manifest createdAt 을 그대로
    startedAt 으로 옮기면 offset 형식(`+00:00`)이 섞일 수 있다. 그 경우 None 으로
    빠지면 --since 필터가 fail-open 으로 모든 row 를 통과시키므로, fromisoformat
    fallback 으로 offset 형식도 받아 naive UTC 로 정규화한다.
    """
    parsed = _parse_iso(ts)
    if parsed is not None:
        return parsed
    try:
        dt = datetime.fromisoformat(ts)
    except (ValueError, TypeError):
        return None
    if dt.tzinfo is not None:
        dt = dt.astimezone(timezone.utc).replace(tzinfo=None)
    return dt


def list_runs(home: Path, *, project: str = "all", task_group: str = "all",
              status: str = "all", since: str = "", limit: int = 0,
              include_archive: bool = False) -> List[dict]:
    """active + recent (옵션 archive) 를 합쳐 필터/정렬 후 반환."""
    rows = read_run_index(home / "active.jsonl") + read_run_index(home / "recent.jsonl")
    if include_archive:
        archive_dir = home / "archive"
        if archive_dir.is_dir():
            for f in sorted(archive_dir.glob("*/*.jsonl")):
                rows.extend(read_run_index(f))
    threshold = _parse_since(since)
    out: List[dict] = []
    for r in rows:
        if project != "all" and r.get("projectId") != project:
            continue
        if task_group != "all" and r.get("taskGroup") != task_group:
            continue
        if status != "all" and r.get("status") != status:
            continue
        if threshold:
            started = _parse_started_at(r.get("startedAt", ""))
            if started and started < threshold:
                continue
        out.append(r)
    out.sort(key=lambda r: r.get("startedAt", ""), reverse=True)
    if limit > 0:
        out = out[:limit]
    return out


def absolute_final_report_path(row: dict) -> Optional[Path]:
    from .final_report_paths import index_report_record_rel

    target = resolve_under_root(row.get("projectRoot", ""), index_report_record_rel(row))
    return target.resolve() if target is not None else None
