#!/usr/bin/env bash
set -euo pipefail

ROOT="${AGENTTEAM_ELECTRON_CUTOVER_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)}"

python3 - "${ROOT}" <<'PY'
from __future__ import annotations

import re
import sys
from pathlib import Path

root = Path(sys.argv[1]).resolve()
violations: list[str] = []


def production_files(base: Path, suffixes: tuple[str, ...]):
    if not base.exists():
        return
    for path in sorted(base.rglob("*")):
        if not path.is_file() or path.suffix not in suffixes:
            continue
        relative = path.relative_to(root).as_posix()
        if any(part in {"node_modules", "dist", "vendor", ".git"} for part in path.parts):
            continue
        if re.search(r"(?:^|[._-])(test|spec)(?:[._-]|$)", path.name):
            continue
        yield path, relative


def reject(path: Path, relative: str, pattern: str, label: str, flags: int = 0):
    text = path.read_text(encoding="utf-8", errors="ignore")
    if re.search(pattern, text, flags):
        violations.append(f"{label}: {relative}")


for dependency in (root / "desktop/go.mod", root / "desktop/go.sum"):
    if dependency.exists():
        reject(
            dependency,
            dependency.relative_to(root).as_posix(),
            r"github\.com/wailsapp|wails/v[0-9]",
            "Wails dependency remains",
            re.I,
        )

for path, relative in production_files(root / "desktop", (".go",)):
    reject(path, relative, r"github\.com/wailsapp", "Wails import remains", re.I)
    reject(
        path,
        relative,
        r'github\.com/tidbcloud/AgentTeam/webassets',
        "webassets import remains",
    )
    reject(path, relative, r"/static/auth\.js", "/static/auth.js remains")
    reject(
        path,
        relative,
        r"/api/internal/auth/login",
        "/api/internal/auth/login remains",
    )
    reject(
        path,
        relative,
        r'/(?:api/internal/)?(?:window(?:/|")|tray/(?:badge|unread)|browser/open|upgrade/(?:check|perform|restart|log))',
        "native UI HTTP route remains",
        re.I,
    )

for retired in (
    root / "webassets/embed.go",
    root / "webassets/embed_stub.go",
    root / "webassets/go.mod",
    root / "desktop/release/client/darwin-arm64/build.sh",
    root / "desktop/release/client/darwin-amd64/build.sh",
    root / "desktop/installer/mac/build.sh",
    root / "desktop/installer/win/build.sh",
    root / "desktop/installer/win/installer.nsi",
):
    if retired.exists():
        violations.append(
            f"retired desktop release path remains: {retired.relative_to(root).as_posix()}"
        )

for base in (root / "desktop/release", root / "desktop/installer"):
    for path, relative in production_files(base, (".sh", ".nsi", ".ps1", ".yml", ".yaml")):
        reject(path, relative, r"webview2", "WebView2 packaging path remains", re.I)

for base in (root / "web/src/runtime/desktop", root / "web/src/entries"):
    for path, relative in production_files(base, (".ts", ".tsx")):
        reject(
            path,
            relative,
            r"window\.location\.(?:origin|host|hostname|port|protocol)|new\s+URL\s*\(\s*window\.location",
            "Desktop host constructed from window.location",
        )

for path, relative in production_files(root / "desktop/electron/src", (".ts", ".tsx")):
    reject(
        path,
        relative,
        r"nodeIntegration\s*:\s*true|contextIsolation\s*:\s*false|sandbox\s*:\s*false",
        "Electron Node integration is enabled",
    )
    reject(
        path,
        relative,
        r'exposeInMainWorld\s*\(\s*["\'](?:ipcRenderer|electron|node|process|fs)["\']|window\.agentteamDesktop\.(?:invoke|send|on)\s*\(',
        "generic Electron bridge is exposed",
    )

ipc_contract = root / "desktop/electron/src/shared/ipc.ts"
if ipc_contract.exists():
    contract_text = ipc_contract.read_text(encoding="utf-8", errors="ignore")
    request_section = contract_text.split("const responseSchemas", 1)[0]
    request_channels = set(
        re.findall(r'z\.literal\(\s*["\'](desktop:[^"\']+)["\']\s*\)', request_section)
    )
    for path, relative in production_files(root / "desktop/electron/src/main", (".ts", ".tsx")):
        text = path.read_text(encoding="utf-8", errors="ignore")
        for channel in re.findall(
            r'ipcMain\.handle\(\s*["\'](desktop:[^"\']+)["\']', text
        ):
            if channel not in request_channels:
                violations.append(
                    f"Electron IPC handler is outside shared request allowlist ({channel}): {relative}"
                )

builder_config = root / "desktop/electron/electron-builder.yml"
mac_icon = root / "desktop/electron/build/icon.icns"
win_icon = root / "desktop/electron/build/icon.ico"
if builder_config.exists():
    builder_text = builder_config.read_text(encoding="utf-8", errors="ignore")
    if not re.search(r"(?ms)^mac:\s*.*?^\s+icon:\s*build/icon\.icns\s*$", builder_text):
        violations.append("Electron macOS branded application icon is not configured")
    if not re.search(r"(?ms)^win:\s*.*?^\s+icon:\s*build/icon\.ico\s*$", builder_text):
        violations.append("Electron Windows branded application icon is not configured")
    if not mac_icon.is_file():
        violations.append("Electron macOS branded application icon is missing")
    if not win_icon.is_file():
        violations.append("Electron Windows branded application icon is missing")

if violations:
    print("Electron cutover guardrail failed:", file=sys.stderr)
    for violation in violations:
        print(f"  - {violation}", file=sys.stderr)
    raise SystemExit(1)

print("Electron cutover guardrail passed.")
PY
