"""Playwright dashboard generator (pw-summary-report.json format).

Fetches build history, enriches each build with pw-summary-report.json
test counts (in parallel), renders and saves the HTML dashboard.
"""
import json
import os
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
from importlib.resources import files
from pathlib import Path

import requests

from .playwright_ops import batch_history


DEFAULT_DAYS = 14


# ---------------------------------------------------------------------------
# Report summary fetching  (requests replaces curl)
# ---------------------------------------------------------------------------

def _summary_url(report_url: str) -> str:
    if "report.html" in report_url:
        return report_url.replace("report.html", "pw-summary-report.json")
    return report_url.rsplit("/", 1)[0] + "/pw-summary-report.json"


def fetch_playwright_summary(report_url: str) -> dict | None:
    try:
        resp = requests.get(_summary_url(report_url), timeout=10)
        if resp.status_code != 200 or not resp.text.strip():
            return None
        return resp.json()
    except (requests.RequestException, ValueError):
        return None


def enrich_builds(builds: list[dict]) -> list[dict]:
    """Fetch pw-summary-report.json for every build in parallel."""

    def _fetch(build: dict) -> tuple[dict, dict | None]:
        return build, fetch_playwright_summary(build["reportUrl"])

    with ThreadPoolExecutor(max_workers=16) as pool:
        futures = {pool.submit(_fetch, b): b for b in builds if b.get("reportUrl")}
        for future in as_completed(futures):
            build, summary = future.result()
            if summary:
                build["testsPassed"]  = summary.get("passedTests",  0)
                build["testsFailed"]  = summary.get("failedTests",  0)
                build["testsFlaky"]   = summary.get("flakyTests",   0)
                build["testsSkipped"] = summary.get("skippedTests", 0)
                build["failedTitles"] = summary.get("failedTestTitles", [])
                build["flakyTitles"]  = summary.get("flakyTestTitles",  [])
            else:
                build.setdefault("testsPassed",  None)
                build.setdefault("testsFailed",  None)
                build.setdefault("testsFlaky",   None)
                build.setdefault("testsSkipped", None)
                build.setdefault("failedTitles", [])
                build.setdefault("flakyTitles",  [])

    return builds


# ---------------------------------------------------------------------------
# HTML generation
# ---------------------------------------------------------------------------

def _load_template() -> str:
    return files("looper_dashboard.templates").joinpath("playwright.html").read_text()


def generate_html(
    builds: list[dict],
    fetched_at: str,
    days: int,
    ai_analysis: list[dict] | None = None,
) -> str:
    rows_json = [
        {
            "jobName":     b["jobName"],
            "flowName":    b["flowName"],
            "buildNumber": b.get("buildNumber"),
            "buildStatus": b.get("buildStatus", "UNKNOWN"),
            "startTime":   b.get("startTime"),
            "duration":    b.get("duration"),
            "testsPassed":  b.get("testsPassed"),
            "testsFailed":  b.get("testsFailed"),
            "testsFlaky":   b.get("testsFlaky"),
            "testsSkipped": b.get("testsSkipped"),
            "reportUrl":    b.get("reportUrl", ""),
            "failedTitles": b.get("failedTitles", []),
            "flakyTitles":  b.get("flakyTitles",  []),
        }
        for b in builds
    ]

    template = _load_template()
    html = template.replace("__ROWS_JSON__",    json.dumps(rows_json))
    html = html.replace("__FETCHED_AT__",       fetched_at)
    html = html.replace("__DAYS__",             str(days))
    html = html.replace("__AI_ANALYSIS_JSON__", json.dumps(ai_analysis or []))
    return html


# ---------------------------------------------------------------------------
# Failure summary
# ---------------------------------------------------------------------------

def save_failure_summary(builds: list[dict], output_path: Path) -> None:
    seen: set[tuple] = set()
    failures = []
    for b in sorted(builds, key=lambda x: x.get("startTime") or "", reverse=True):
        key = (b["jobName"], b["flowName"])
        if key in seen or b.get("buildStatus") != "FAILURE":
            continue
        failed = b.get("failedTitles", [])
        flaky  = b.get("flakyTitles",  [])
        if not failed and not flaky:
            continue
        seen.add(key)
        failures.append({
            "job":         b["jobName"].split("/")[-1],
            "flow":        b["flowName"],
            "buildNumber": b.get("buildNumber"),
            "passed":      b.get("testsPassed",  0),
            "failed":      b.get("testsFailed",  0),
            "flaky":       b.get("testsFlaky",   0),
            "skipped":     b.get("testsSkipped", 0),
            "reportUrl":   b.get("reportUrl", ""),
            "failedTests": failed,
            "flakyTests":  flaky,
        })

    if failures:
        output_path.parent.mkdir(parents=True, exist_ok=True)
        output_path.write_text(json.dumps(failures, indent=2))
        print(f"Saved {len(failures)} failure summaries to {output_path}", file=sys.stderr)


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------

def run(
    config: str,
    output: str,
    scm_url: str | None = None,
    days: int = DEFAULT_DAYS,
    no_details: bool = False,
    save_failures: str | None = None,
    analysis_json: str | None = None,
    auto_open: bool = False,
) -> bool:
    """Run the full Playwright dashboard generation pipeline.

    Returns True on success.
    """
    print(f"Fetching last {days} days of Playwright build history...", file=sys.stderr)
    result = batch_history(config_path=config, scm_url=scm_url, days=days)
    builds = result["builds"]
    fetched_at = result["fetchedAt"]

    jobs_seen = len({b["jobName"] for b in builds})
    print(f"   Found {len(builds)} builds across {jobs_seen} jobs", file=sys.stderr)

    if not no_details:
        print(f"Fetching Playwright summaries ({len(builds)} parallel requests)...", file=sys.stderr)
        builds = enrich_builds(builds)

    failures_path = Path(save_failures or "playwright-failures.json")
    save_failure_summary(builds, failures_path)

    ai_analysis = None
    if analysis_json:
        p = Path(analysis_json)
        if p.exists():
            ai_analysis = json.loads(p.read_text())
            print(f"Loaded AI analysis from {p}", file=sys.stderr)
        else:
            print(f"Warning: --analysis-json not found: {p}", file=sys.stderr)

    print("Generating Playwright dashboard...", file=sys.stderr)
    html = generate_html(builds, fetched_at, days, ai_analysis=ai_analysis)

    output_path = Path(output)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    output_path.write_text(html)
    print(f"Dashboard saved to {output_path.resolve()}", file=sys.stderr)

    if auto_open:
        if sys.platform == "darwin":
            os.system(f"open '{output_path}'")
        elif sys.platform == "win32":
            os.startfile(str(output_path))  # noqa: S606
        else:
            os.system(f"xdg-open '{output_path}'")

    return True
