"""Installer widget — "Instalar J6412" tab in AR-Autopilot Studio.

Lets the integrator build a USB pendrive installer image without leaving
the Studio:

  1. Enter vessel name + generate (or paste) a serial number.
  2. Choose which apps to bundle (AR-ECDIS, AR-Autopilot Display).
  3. Click "Build USB Image" → runs installer/build_usb.py in a worker thread.
  4. Watch the live log.  When done, open the dist/ folder.

RBAC: only Engineer and Super Admin can build installers.
"""

from __future__ import annotations

import os
import secrets
import shlex
import subprocess
import sys
from pathlib import Path

from PySide6.QtCore import QObject, QThread, Signal
from PySide6.QtWidgets import (
    QCheckBox,
    QFileDialog,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QMessageBox,
    QPlainTextEdit,
    QPushButton,
    QVBoxLayout,
    QWidget,
)

from arautopilot.core.rbac import Capability
from arautopilot.studio.session import Session

REPO_ROOT     = Path(__file__).resolve().parents[2]
INSTALLER_DIR = REPO_ROOT / "installer"
DIST_DIR      = INSTALLER_DIR / "dist"
BUILD_SCRIPT  = INSTALLER_DIR / "build_usb.py"


# ---------------------------------------------------------------------------
#  Serial generator (inline — no dependency on installer/serial_generator.py)
# ---------------------------------------------------------------------------

def _generate_serial() -> str:
    raw = secrets.token_hex(6).upper()
    return f"AR-{raw[0:4]}-{raw[4:8]}-{raw[8:12]}"


# ---------------------------------------------------------------------------
#  Worker thread
# ---------------------------------------------------------------------------

class _BuildWorker(QObject):
    line      = Signal(str)
    finished  = Signal(int)   # exit code
    error     = Signal(str)

    def __init__(self, argv: list[str]) -> None:
        super().__init__()
        self._argv = argv
        self._proc: subprocess.Popen | None = None
        self._cancelled = False

    def run(self) -> None:
        try:
            env = os.environ.copy()
            env["PYTHONIOENCODING"] = "utf-8"
            self._proc = subprocess.Popen(
                self._argv,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                env=env,
            )
            assert self._proc.stdout
            for raw in self._proc.stdout:
                if self._cancelled:
                    break
                self.line.emit(raw.rstrip("\n"))
            code = self._proc.wait()
            self.finished.emit(code)
        except FileNotFoundError as exc:
            self.error.emit(f"No se encontró el ejecutable: {exc}")
        except Exception as exc:  # noqa: BLE001
            self.error.emit(str(exc))

    def cancel(self) -> None:
        self._cancelled = True
        if self._proc:
            try:
                self._proc.terminate()
            except Exception:
                pass


# ---------------------------------------------------------------------------
#  Widget
# ---------------------------------------------------------------------------

class InstallerWidget(QWidget):
    """'Instalar J6412' tab content."""

    def __init__(self, session: Session, parent: QWidget | None = None) -> None:
        super().__init__(parent)
        self._session = session
        self._thread: QThread | None = None
        self._worker: _BuildWorker | None = None
        self._build_ui()

    # ── UI ───────────────────────────────────────────────────────────────────

    def _build_ui(self) -> None:
        root = QVBoxLayout(self)
        root.setContentsMargins(12, 12, 12, 12)
        root.setSpacing(12)

        # Header
        hdr = QLabel(
            "<b style='color:#4A9FE8;font-size:14px;letter-spacing:1px'>"
            "INSTALAR EN J6412</b><br/>"
            "<span style='color:#8899AA;font-size:11px'>"
            "Genera un pendrive USB con las apps AR Electronics y la licencia del buque.</span>"
        )
        hdr.setTextFormat(1)  # RichText
        hdr.setWordWrap(True)
        root.addWidget(hdr)

        # ── Config group ─────────────────────────────────────────────────────
        cfg = QGroupBox("Configuración del paquete")
        form = QVBoxLayout(cfg)

        # Vessel name
        vessel_row = QHBoxLayout()
        vessel_row.addWidget(QLabel("Nombre del buque:"))
        self._vessel_edit = QLineEdit()
        self._vessel_edit.setPlaceholderText("e.g.  M/Y PACIFICO")
        vessel_row.addWidget(self._vessel_edit, 1)
        form.addLayout(vessel_row)

        # Serial number
        serial_row = QHBoxLayout()
        serial_row.addWidget(QLabel("Número de serie:"))
        self._serial_edit = QLineEdit()
        self._serial_edit.setPlaceholderText("AR-XXXX-XXXX-XXXX")
        self._serial_edit.setMaximumWidth(220)
        gen_btn = QPushButton("Generar")
        gen_btn.setToolTip("Genera un nuevo número de serie aleatorio")
        gen_btn.clicked.connect(self._on_generate_serial)
        serial_row.addWidget(self._serial_edit)
        serial_row.addWidget(gen_btn)
        serial_row.addStretch(1)
        form.addLayout(serial_row)

        # CSV log
        csv_row = QHBoxLayout()
        csv_row.addWidget(QLabel("Registro CSV:"))
        self._csv_edit = QLineEdit()
        self._csv_edit.setPlaceholderText("Opcional — ruta al archivo serials.csv")
        browse_btn = QPushButton("…")
        browse_btn.setFixedWidth(30)
        browse_btn.clicked.connect(self._on_browse_csv)
        csv_row.addWidget(self._csv_edit, 1)
        csv_row.addWidget(browse_btn)
        form.addLayout(csv_row)

        # App checkboxes
        app_row = QHBoxLayout()
        self._chk_autopilot = QCheckBox("AR-Autopilot Display (Flutter)")
        self._chk_autopilot.setChecked(True)
        self._chk_ecdis = QCheckBox("AR-ECDIS")
        self._chk_ecdis.setChecked(True)
        self._chk_no_flutter = QCheckBox("Omitir compilación Flutter (usar build existente)")
        app_row.addWidget(self._chk_autopilot)
        app_row.addWidget(self._chk_ecdis)
        app_row.addStretch(1)
        form.addLayout(app_row)
        form.addWidget(self._chk_no_flutter)

        root.addWidget(cfg)

        # ── Action row ───────────────────────────────────────────────────────
        act = QHBoxLayout()
        self._build_btn = QPushButton("▶  Build USB Image")
        self._build_btn.setObjectName("primary")
        self._build_btn.setToolTip("Compila e empaqueta el instalador USB")
        self._build_btn.clicked.connect(self._on_build)
        act.addWidget(self._build_btn)

        self._cancel_btn = QPushButton("Cancelar")
        self._cancel_btn.setEnabled(False)
        self._cancel_btn.clicked.connect(self._on_cancel)
        act.addWidget(self._cancel_btn)

        self._open_btn = QPushButton("Abrir dist/")
        self._open_btn.setEnabled(False)
        self._open_btn.setToolTip(f"Abre la carpeta: {DIST_DIR}")
        self._open_btn.clicked.connect(self._on_open_dist)
        act.addWidget(self._open_btn)

        act.addStretch(1)
        self._status_lbl = QLabel("")
        act.addWidget(self._status_lbl)
        root.addLayout(act)

        # ── Log ──────────────────────────────────────────────────────────────
        self._log = QPlainTextEdit()
        self._log.setReadOnly(True)
        self._log.setStyleSheet(
            "background:#0A1520; color:#C9D1D9; font-family:Consolas,'Cascadia Mono',monospace; font-size:10px;"
        )
        self._log.setPlaceholderText("El log del proceso de build aparecerá aquí…")
        root.addWidget(self._log, 1)

        # RBAC gate
        can_build = self._session.has(Capability.EDIT_COMMISSIONING)
        if not can_build:
            self._build_btn.setEnabled(False)
            self._build_btn.setToolTip("Requiere rol Engineer o Super Admin.")
            self._vessel_edit.setEnabled(False)
            self._serial_edit.setEnabled(False)
            self._chk_autopilot.setEnabled(False)
            self._chk_ecdis.setEnabled(False)
            self._chk_no_flutter.setEnabled(False)
            root.insertWidget(0, QLabel(
                "⚠  Tu rol no permite construir instaladores. "
                "Inicia sesión como Engineer o Super Admin."
            ))

    # ── Handlers ──────────────────────────────────────────────────────────────

    def _on_generate_serial(self) -> None:
        self._serial_edit.setText(_generate_serial())

    def _on_browse_csv(self) -> None:
        path, _ = QFileDialog.getSaveFileName(
            self, "Registro de seriales", str(Path.home() / "serials.csv"),
            "CSV (*.csv);;Todos los archivos (*)"
        )
        if path:
            self._csv_edit.setText(path)

    def _on_build(self) -> None:
        vessel = self._vessel_edit.text().strip()
        serial = self._serial_edit.text().strip().upper()

        if not vessel:
            QMessageBox.warning(self, "Falta dato", "Introduce el nombre del buque.")
            return
        if not serial:
            serial = _generate_serial()
            self._serial_edit.setText(serial)
            self._log.appendPlainText(f"[auto] Serial generado: {serial}")

        if not BUILD_SCRIPT.exists():
            QMessageBox.critical(
                self, "Script no encontrado",
                f"No se encontró:\n{BUILD_SCRIPT}\n\nVerifica que el repo esté completo."
            )
            return

        argv = [sys.executable, str(BUILD_SCRIPT), "--vessel", vessel, "--serial", serial]
        if not self._chk_autopilot.isChecked() or self._chk_no_flutter.isChecked():
            argv.append("--no-flutter")
        if not self._chk_ecdis.isChecked():
            argv.append("--no-ecdis")
        csv = self._csv_edit.text().strip()
        if csv:
            argv += ["--csv", csv]

        self._log.clear()
        self._log.appendPlainText(f"$ {' '.join(shlex.quote(a) for a in argv)}\n")
        self._set_running(True)
        self._status_lbl.setText("Building…")

        self._thread = QThread(self)
        self._worker = _BuildWorker(argv)
        self._worker.moveToThread(self._thread)
        self._thread.started.connect(self._worker.run)
        self._worker.line.connect(self._log.appendPlainText)
        self._worker.finished.connect(self._on_build_done)
        self._worker.error.connect(self._on_build_error)
        self._worker.finished.connect(self._thread.quit)
        self._worker.error.connect(self._thread.quit)
        self._thread.finished.connect(self._cleanup_thread)
        self._thread.start()

    def _on_cancel(self) -> None:
        if self._worker:
            self._worker.cancel()
        self._log.appendPlainText("\n[cancelado por el operador]")

    def _on_open_dist(self) -> None:
        if DIST_DIR.exists():
            os.startfile(str(DIST_DIR))  # Windows Explorer
        else:
            QMessageBox.information(self, "Sin carpeta", f"No existe:\n{DIST_DIR}")

    def _on_build_done(self, code: int) -> None:
        if code == 0:
            self._status_lbl.setText("✓ Build completado")
            self._open_btn.setEnabled(True)
            self._log.appendPlainText(
                f"\n✓ Pendrive listo en:\n{DIST_DIR}\n"
                "Copie TODO el contenido de dist/ al pendrive USB."
            )
        else:
            self._status_lbl.setText(f"✗ Error (exit {code})")
        self._set_running(False)

    def _on_build_error(self, msg: str) -> None:
        self._log.appendPlainText(f"\n[ERROR] {msg}")
        self._status_lbl.setText("✗ Error")
        self._set_running(False)

    def _set_running(self, running: bool) -> None:
        can_build = self._session.has(Capability.EDIT_COMMISSIONING)
        self._build_btn.setEnabled(not running and can_build)
        self._cancel_btn.setEnabled(running)

    def _cleanup_thread(self) -> None:
        if self._thread:
            self._thread.deleteLater()
            self._thread = None
        if self._worker:
            self._worker.deleteLater()
            self._worker = None
