"""Win11 OHD Staging / Live / Rollback helpers for /ohd/setup."""
from __future__ import annotations

import json
import os
import subprocess
import sys
from pathlib import Path
from typing import Any, Dict, Optional


def live_root(app_root: Optional[Path] = None) -> Path:
    return (app_root or Path(__file__).resolve().parent).resolve()


def staging_root(app_root: Optional[Path] = None) -> Path:
    live = live_root(app_root)
    return live.parent / "DyzineFrame-Staging"


def rollback_root(app_root: Optional[Path] = None) -> Path:
    live = live_root(app_root)
    return live.parent / "DyzineFrame-Rollback"


def pending_flag(app_root: Optional[Path] = None) -> Path:
    return live_root(app_root) / "run" / "staging-pending"


def _read_version(root: Path) -> str:
    path = root / "VERSION.txt"
    try:
        return path.read_text(encoding="utf-8").splitlines()[0].strip()
    except (OSError, IndexError):
        return ""


def _stamp_tuple(stamp: str):
    parts = []
    for tok in (stamp or "").strip().split("."):
        if not tok.isdigit():
            return None
        parts.append(int(tok))
    if not parts:
        return None
    while len(parts) < 4:
        parts.append(0)
    return tuple(parts[:4])


def _is_older(left: str, right: str) -> bool:
    a, b = _stamp_tuple(left), _stamp_tuple(right)
    if a is None or b is None:
        return False
    return a < b


def ota_status(app_root: Optional[Path] = None) -> Dict[str, Any]:
    live = live_root(app_root)
    staging = staging_root(app_root)
    rollback = rollback_root(app_root)
    lv = _read_version(live)
    sv = _read_version(staging)
    rv = _read_version(rollback)
    pending = pending_flag(app_root).is_file()
    older = bool(sv and lv and _is_older(sv, lv))
    if pending and older:
        pending = False
    return {
        "platform": sys.platform,
        "live_root": str(live),
        "staging_root": str(staging),
        "rollback_root": str(rollback),
        "live_version": lv,
        "staging_version": sv,
        "rollback_version": rv,
        "pending": pending,
        "staging_older_than_live": older,
        "win11": sys.platform == "win32",
    }


def _sync_dir(app_root: Optional[Path] = None) -> Path:
    live = live_root(app_root)
    cand = live / "sync"
    if (cand / "stage_ohd_from_dropbox.ps1").is_file():
        return cand
    # Workshop layout
    tools = live.parent.parent / "Deploy Tools" / "ohd-win11" / "sync"
    if (tools / "stage_ohd_from_dropbox.ps1").is_file():
        return tools
    return cand


def _run_ps1(script: Path, args: list[str], *, timeout_s: int = 600) -> Dict[str, Any]:
    if sys.platform != "win32":
        return {"ok": False, "error": "Win11 OTA scripts are Windows-only"}
    if not script.is_file():
        return {"ok": False, "error": f"missing {script}"}
    cmd = [
        "powershell",
        "-NoProfile",
        "-ExecutionPolicy",
        "Bypass",
        "-File",
        str(script),
        *args,
    ]
    try:
        proc = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=timeout_s,
            check=False,
        )
    except (OSError, subprocess.TimeoutExpired) as exc:
        return {"ok": False, "error": str(exc)}
    out = (proc.stdout or "") + (proc.stderr or "")
    return {
        "ok": proc.returncode == 0,
        "exit_code": proc.returncode,
        "output": out[-4000:],
        "status": ota_status(),
    }


def check_for_updates(app_root: Optional[Path] = None, *, offline: bool = False) -> Dict[str, Any]:
    live = live_root(app_root)
    script = _sync_dir(app_root) / "stage_ohd_from_dropbox.ps1"
    args = ["-LiveRoot", str(live), "-NoPause", "-Quiet"]
    if offline:
        args.append("-Offline")
    return _run_ps1(script, args)


def promote_now(app_root: Optional[Path] = None, *, restart: bool = True) -> Dict[str, Any]:
    live = live_root(app_root)
    script = _sync_dir(app_root) / "promote_ohd_staging.ps1"
    args = ["-LiveRoot", str(live), "-Force", "-NoPause"]
    if restart:
        args.append("-Restart")
    return _run_ps1(script, args)


def rollback_now(app_root: Optional[Path] = None, *, restart: bool = True) -> Dict[str, Any]:
    live = live_root(app_root)
    script = _sync_dir(app_root) / "rollback_ohd.ps1"
    args = ["-LiveRoot", str(live), "-NoPause"]
    if restart:
        args.append("-Restart")
    return _run_ps1(script, args)


def status_json(app_root: Optional[Path] = None) -> str:
    return json.dumps(ota_status(app_root))
