#!/usr/bin/env python3
"""Smoke-test web/ui_window_review.html in WebKit."""
from __future__ import annotations

import argparse
import json
import shutil
import subprocess
import time
from pathlib import Path
from urllib.parse import urljoin

from browser_smoke_helpers import (
    WebDriverError,
    execute_js,
    free_port,
    request_json,
    wait_for_driver,
)


ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "out"


def state_script() -> str:
    return """
return {
  readyState: document.readyState,
  marker: window.HWANSE_UI_WINDOW_REVIEW_READY || null,
  exeAssembly: window.HWANSE_UI_WINDOW_EXE_ASSEMBLY_READY || null,
  assetTitle: document.getElementById('assetTitle')?.textContent || '',
  assetStatus: document.getElementById('assetStatus')?.textContent || '',
  assetButtonCount: document.querySelectorAll('.asset-button').length,
  gridOptions: [...document.querySelectorAll('#gridSelect option')].map((node) => node.textContent || ''),
  rectRows: document.querySelectorAll('#rectRows tr').length,
  nineSliceCards: document.querySelectorAll('#nineSliceGrid .preview-card').length,
  windowComposerVisible: !document.getElementById('windowComposerPanel')?.hidden,
  windowGridSummary: document.getElementById('windowGridSummary')?.textContent || '',
  sourceCanvas: {
    width: document.getElementById('sourceCanvas')?.width || 0,
    height: document.getElementById('sourceCanvas')?.height || 0,
  },
  exeTemplateCards: document.querySelectorAll('#exeTemplateGrid .template-card').length,
  exeScreenLayoutCards: document.querySelectorAll('#exeScreenLayoutGrid .screen-layout-card').length,
  exeAssemblyStatus: document.getElementById('exeAssemblyStatus')?.textContent || '',
  exeAssemblyCanvas: {
    width: document.getElementById('exeAssemblyCanvas')?.width || 0,
    height: document.getElementById('exeAssemblyCanvas')?.height || 0,
  },
};
"""


def wait_for_review(port: int, session_id: str, expected_asset: str, timeout: float = 8) -> dict:
    deadline = time.monotonic() + timeout
    last_state = {}
    while time.monotonic() < deadline:
        state = execute_js(port, session_id, state_script(), timeout=3)
        last_state = state
        marker = state.get("marker") or {}
        assembly = state.get("exeAssembly") or {}
        assembly_ready = (
            assembly.get("status") == "ok"
            and assembly.get("templates") == 17
            and assembly.get("windowRegionBindings") == 20
            and assembly.get("screenLayoutPresets") == 13
            and state.get("exeTemplateCards") == 17
            and state.get("exeScreenLayoutCards") == 13
            and (state.get("exeAssemblyCanvas") or {}).get("width") == 640
            and (state.get("exeAssemblyCanvas") or {}).get("height") == 608
        )
        if state.get("readyState") == "complete" and marker.get("browserUiWindowReviewImplemented") is True:
            if expected_asset == "window" and (
                marker.get("asset") == "window"
                and marker.get("selectedRectSource") == "confirmed-16x16-cell-grid"
                and marker.get("transparentRgb") == [99, 0, 107]
                and marker.get("cellCount") == 72
                and marker.get("windowCellGridComposerImplemented") is True
                and state.get("rectRows") == 72
                and state.get("nineSliceCards") == 0
                and state.get("windowComposerVisible") is True
                and assembly_ready
            ):
                return state
            if expected_asset == "num" and (
                marker.get("asset") == "num"
                and marker.get("selectedRectSource") == "confirmed-glyph-grid"
                and marker.get("glyphCount") == 40
                and marker.get("transparentRgb") == [99, 0, 107]
                and state.get("rectRows") == 40
                and state.get("nineSliceCards") == 0
            ):
                return state
            if expected_asset == "status" and (
                marker.get("asset") == "status"
                and marker.get("selectedRectSource") == "review-full-image"
                and marker.get("rectCount") == 1
                and marker.get("transparentRgb") == [99, 0, 107]
                and state.get("rectRows") == 1
                and state.get("nineSliceCards") == 0
            ):
                return state
            if expected_asset == "face_01" and (
                marker.get("asset") == "face_01"
                and marker.get("selectedRectSource") == "confirmed-80x80-face-grid"
                and marker.get("cellCount") == 16
                and isinstance(marker.get("transparentRgb"), list)
                and state.get("rectRows") == 16
                and state.get("nineSliceCards") == 0
            ):
                return state
            if expected_asset == "01234567" and (
                marker.get("asset") == "01234567"
                and marker.get("selectedRectSource") == "review-full-image"
                and marker.get("rectCount") == 1
                and isinstance(marker.get("transparentRgb"), list)
                and state.get("rectRows") == 1
                and state.get("nineSliceCards") == 0
            ):
                return state
            if expected_asset == "frame" and (
                marker.get("asset") == "frame"
                and marker.get("selectedRectSource") == "confirmed-full-frame"
                and (marker.get("fullFrame") or {}).get("w") == 640
                and (marker.get("fullFrame") or {}).get("h") == 352
                and marker.get("transparentRgb") == [99, 0, 107]
                and state.get("rectRows") == 1
                and state.get("nineSliceCards") == 0
            ):
                return state
        time.sleep(0.2)
    raise WebDriverError(f"ui window review did not become ready: {last_state!r}")


def verify_browser(base: str, asset: str = "window", keep_log: bool = False) -> dict:
    driver_path = shutil.which("WebKitWebDriver")
    if not driver_path:
        raise WebDriverError("WebKitWebDriver is not installed; install webkit2gtk-driver and run under Xvfb")

    port = free_port()
    log_path = OUT / "ui_window_review_webkitdriver.log"
    log_path.parent.mkdir(parents=True, exist_ok=True)
    with log_path.open("wb") as log:
        proc = subprocess.Popen(
            [
                driver_path,
                "--host=127.0.0.1",
                f"--port={port}",
                "--replace-on-new-session",
            ],
            stdout=log,
            stderr=subprocess.STDOUT,
        )
        session_id = ""
        try:
            wait_for_driver(port, proc)
            response = request_json(
                port,
                "POST",
                "/session",
                {"capabilities": {"alwaysMatch": {"browserName": "MiniBrowser"}}},
                timeout=60,
            )
            value = (response or {}).get("value") or {}
            session_id = value.get("sessionId") or ""
            if not session_id:
                raise WebDriverError(f"could not create WebDriver session: {response!r}")
            url = urljoin(base, f"/web/ui_window_review.html?asset={asset}")
            request_json(port, "POST", f"/session/{session_id}/url", {"url": url}, timeout=30)
            state = wait_for_review(port, session_id, asset)
        finally:
            if session_id:
                try:
                    request_json(port, "DELETE", f"/session/{session_id}", timeout=2)
                except Exception:
                    pass
            proc.terminate()
            try:
                proc.wait(timeout=4)
            except subprocess.TimeoutExpired:
                proc.kill()
            if not keep_log:
                log_path.unlink(missing_ok=True)
    return state


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--base", default="http://127.0.0.1:8013")
    parser.add_argument("--asset", choices=["window", "num", "status", "frame", "face_01", "01234567"], default="window")
    parser.add_argument("--keep-log", action="store_true")
    args = parser.parse_args()
    state = verify_browser(args.base.rstrip("/") + "/", asset=args.asset, keep_log=args.keep_log)
    OUT.mkdir(parents=True, exist_ok=True)
    (OUT / "ui_window_review_browser_smoke.json").write_text(
        json.dumps(state, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )
    print(json.dumps({"status": "ok", "asset": state["marker"]["asset"], "rectRows": state["rectRows"], "nineSliceCards": state["nineSliceCards"]}, ensure_ascii=False))


if __name__ == "__main__":
    main()
