"""Playwright-specific Looper MCP operations (pw-summary-report.json format).

Builds on the shared looper_core utilities and adds:
  - Playwright CDC report URL builder (playwright-report/report.html)
  - batch_status and batch_history for Playwright flows
"""
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timezone

from .looper_auth import CDC_BASE, http_head_ok
from .looper_core import (
    get_build_history,
    get_job_by_name,
    list_jobs,
    resolve_scm_url,
    search_flow,
)

__all__ = [
    "build_report_url",
    "find_report",
    "batch_status",
    "batch_history",
    "resolve_scm_url",
    "list_jobs",
    "get_job_by_name",
    "get_build_history",
    "search_flow",
]


# ---------------------------------------------------------------------------
# Playwright report URL
# ---------------------------------------------------------------------------

def build_report_url(job_name: str, build_number: int) -> str:
    """Construct the CDC report URL for a Playwright build."""
    return f"{CDC_BASE}/{job_name}/{build_number}/playwright-report/report.html"


# ---------------------------------------------------------------------------
# Flow-entry helpers
# ---------------------------------------------------------------------------

def _best_flow_entry(job_name: str, build: dict) -> dict:
    report_url = build_report_url(job_name, build["buildNumber"])
    return {
        "flowName": build.get("flowName"),
        "buildNumber": build["buildNumber"],
        "buildStatus": build.get("buildStatus"),
        "reportUrl": report_url,
        "reportJsonUrl": report_url.replace("report.html", "pw-summary-report.json"),
    }


def _fill_missing_flows(
    job: dict,
    job_name: str,
    missing: set[str],
    best_by_flow: dict[str, dict],
    flows: list[dict],
) -> None:
    if not missing or not job.get("id"):
        return
    history = get_build_history(job["id"], size=50)
    for build in history:
        bf = build.get("flowName")
        if bf not in missing:
            continue
        bnum = build["buildNumber"]
        if bf not in best_by_flow or bnum > best_by_flow[bf]["buildNumber"]:
            entry = _best_flow_entry(job_name, build)
            best_by_flow[bf] = entry
            flows.append(entry)
        missing.discard(bf)
        if not missing:
            break


# ---------------------------------------------------------------------------
# find-report
# ---------------------------------------------------------------------------

def find_report(
    job_name: str | None = None,
    job_id: str | None = None,
    flow_name: str | None = None,
    scm_url: str | None = None,
    branch: str = "main",
) -> dict:
    """Find the latest Playwright report URL."""
    if not job_id and job_name:
        job = get_job_by_name(job_name)
        if job:
            job_id = job["id"]
            job_name = job["name"]
        else:
            matching = [j for j in list_jobs(resolve_scm_url(scm_url)) if job_name.lower() in j["name"].lower()]
            if matching:
                job_id = matching[0]["id"]
                job_name = matching[0]["name"]

    if not job_id and flow_name:
        matches = search_flow(flow_name, scm_url=scm_url)
        if matches:
            job_id = matches[0]["jobId"]
            job_name = matches[0]["jobName"]

    if not job_id:
        return {"error": "Could not resolve job. Check --job-name or --flow."}

    builds = get_build_history(job_id, branch=branch, flow_name=flow_name, size=50)
    if not builds:
        return {"error": f"No builds found for flow '{flow_name}' in '{job_name}'"}

    latest = builds[0]
    if not job_name:
        job_name = latest.get("jobName", "unknown")

    report_url = build_report_url(job_name, latest["buildNumber"])
    return {
        "jobId": job_id,
        "jobName": job_name,
        "buildNumber": latest["buildNumber"],
        "flowName": latest.get("flowName"),
        "buildStatus": latest.get("buildStatus"),
        "duration": latest.get("duration"),
        "reportUrl": report_url,
        "reportJsonUrl": report_url.replace("report.html", "pw-summary-report.json"),
    }


# ---------------------------------------------------------------------------
# batch-status
# ---------------------------------------------------------------------------

def batch_status(
    scm_url: str | None = None,
    config_path: str | None = None,
) -> dict:
    """Get the latest build status for all monitored Playwright jobs."""
    import yaml

    monitored: dict[str, list[str]] | None = None
    config_scm_url: str | None = None

    if config_path:
        with open(config_path) as f:
            cfg = yaml.safe_load(f)
        monitored = cfg.get("jobs", {})
        config_scm_url = cfg.get("scm_url")

    effective_url = resolve_scm_url(scm_url or config_scm_url)
    all_raw_jobs = list_jobs(effective_url)

    if monitored is not None:
        monitored_names = set(monitored.keys())
        all_jobs = [j for j in all_raw_jobs if j["name"] in monitored_names]
        for name in monitored_names - {j["name"] for j in all_jobs}:
            print(f"Warning: job '{name}' not found in LooperPro.", file=sys.stderr)
    else:
        all_jobs = all_raw_jobs

    results = []
    for job in all_jobs:
        job_name = job["name"]
        target_flows = set(monitored[job_name]) if monitored else None

        best_by_flow: dict[str, dict] = {}
        for b in job["latestBuilds"]:
            flow = b.get("flowName")
            if target_flows and flow not in target_flows:
                continue
            bnum = b.get("buildNumber", 0)
            if flow not in best_by_flow or bnum > best_by_flow[flow]["buildNumber"]:
                best_by_flow[flow] = _best_flow_entry(job_name, {**b, "buildNumber": bnum})
        flows = list(best_by_flow.values())

        if target_flows:
            missing = target_flows - {f["flowName"] for f in flows}
            _fill_missing_flows(job, job_name, missing, best_by_flow, flows)

        if flows:
            results.append({"jobName": job_name, "jobId": job.get("id"), "flows": flows})

    return {"jobs": results, "fetchedAt": datetime.now(timezone.utc).isoformat()}


# ---------------------------------------------------------------------------
# batch-history
# ---------------------------------------------------------------------------

def batch_history(
    config_path: str | None = None,
    scm_url: str | None = None,
    days: int = 14,
) -> dict:
    """Fetch build history for all monitored Playwright jobs within a lookback window."""
    import yaml

    monitored: dict[str, list[str]] = {}
    config_scm_url: str | None = None

    if config_path:
        with open(config_path) as f:
            cfg = yaml.safe_load(f)
        monitored = cfg.get("jobs", {})
        config_scm_url = cfg.get("scm_url")

    effective_url = resolve_scm_url(scm_url or config_scm_url)
    cutoff = datetime.now(timezone.utc).timestamp() - days * 86400

    all_raw_jobs = list_jobs(effective_url)
    monitored_names = set(monitored.keys())
    job_id_by_name: dict[str, str] = {
        j["name"]: j["id"]
        for j in all_raw_jobs
        if j["name"] in monitored_names and j.get("id")
    }
    for name in monitored_names - set(job_id_by_name.keys()):
        print(f"Warning: job '{name}' not found in LooperPro — skipping.", file=sys.stderr)

    def _fetch_job_history(job_name: str, job_id: str, target_flows: list[str]) -> list[dict]:
        history = sorted(
            (b for b in get_build_history(job_id, size=100) if b.get("buildNumber") not in (None, 0)),
            key=lambda b: b["buildNumber"],
            reverse=True,
        )
        entries = []
        for build in history:
            flow = build.get("flowName")
            if flow not in target_flows:
                continue
            start_iso = build.get("startTime")
            if start_iso:
                try:
                    ts = datetime.fromisoformat(start_iso.replace("Z", "+00:00")).timestamp()
                    if ts < cutoff:
                        continue
                except ValueError:
                    pass
            status = build.get("buildStatus", "")
            if status in ("ABORTED", "Accepted", "IN_PROGRESS"):
                continue
            url = build_report_url(job_name, build["buildNumber"])
            if not http_head_ok(url):
                continue
            entries.append({
                "jobName":     job_name,
                "flowName":    flow,
                "buildNumber": build["buildNumber"],
                "buildStatus": status,
                "startTime":   start_iso,
                "duration":    build.get("duration"),
                "reportUrl":   url,
            })
        return entries

    all_builds: list[dict] = []
    with ThreadPoolExecutor(max_workers=8) as pool:
        futures = {
            pool.submit(_fetch_job_history, jn, jid, monitored[jn]): jn
            for jn, jid in job_id_by_name.items()
        }
        for future in as_completed(futures):
            all_builds.extend(future.result())

    all_builds.sort(key=lambda b: b.get("startTime") or "", reverse=True)
    return {"builds": all_builds, "fetchedAt": datetime.now(timezone.utc).isoformat(), "days": days}
