"""OHD Win11 setup UI — pick a local Topbox Pi on the LAN."""
from __future__ import annotations

import json
import socket
from typing import Callable, Optional

from extended_configparser.parser import ExtendedConfigParser
from machine_identity import read_machine_identity
from topbox_discovery import scan_local_topbox_devices


def build_topbox_info_payload(
    *,
    version: str,
    config_path: str,
    display_mode: str,
    asset_number: int,
) -> dict:
    config = ExtendedConfigParser()
    config.read(config_path)
    identity = read_machine_identity(config)
    hostname = socket.gethostname()
    short = hostname.split(".")[0]
    return {
        "version": version,
        "hostname": short,
        "machine_role": identity["machine_role"],
        "bank_number": identity["bank_number"],
        "term_number": identity["term_number"],
        "asset_number": asset_number,
        "display_mode": display_mode,
        "topbox_host": identity["topbox_host"],
    }


def register_ohd_setup_routes(
    *,
    get,
    post,
    request,
    response,
    config_path: str,
    version: str,
    web_port: Callable[[], int],
    display_mode: Callable[[], str],
    asset_number: Callable[[], int],
    reload_config: Callable[[], None],
) -> None:
    @get("/Topbox/info.json")
    def topbox_info_handler():
        payload = build_topbox_info_payload(
            version=version,
            config_path=config_path,
            display_mode=display_mode(),
            asset_number=asset_number(),
        )
        response.content_type = "application/json"
        return json.dumps(payload)

    @get("/ohd/api/topbox-scan")
    def ohd_topbox_scan_handler():
        devices, subnets = scan_local_topbox_devices(port=web_port())
        response.content_type = "application/json"
        return json.dumps({"devices": devices, "scanned_subnets": subnets})

    @get("/ohd/setup")
    def ohd_setup_page_handler():
        config = ExtendedConfigParser()
        config.read(config_path)
        current_source = ""
        current_host = ""
        if config.has_option("TOPBOX", "PROGRESSIVES_SOURCE"):
            current_source = config.get("TOPBOX", "PROGRESSIVES_SOURCE").strip()
        identity = read_machine_identity(config)
        current_host = identity["topbox_host"]
        port = web_port()
        return _OHD_SETUP_HTML.format(
            port=port,
            current_source=_html_escape(current_source),
            current_host=_html_escape(current_host),
        )

    @post("/ohd/setup")
    def ohd_setup_save_handler():
        ip = str(request.forms.get("ip") or "").strip()
        hostname = str(request.forms.get("hostname") or "").strip()
        progressives_url = str(request.forms.get("progressives_url") or "").strip()
        if not progressives_url and ip:
            progressives_url = f"http://{ip}:{web_port()}/EtherGold/progressives.csv"
        if not progressives_url.startswith("http"):
            return _setup_result("FAIL — invalid progressives URL", ok=False)

        config = ExtendedConfigParser()
        config.read(config_path)
        config.set("TOPBOX", "PROGRESSIVES_SOURCE", progressives_url)
        if hostname:
            config.set("TOPBOX", "TOPBOX_HOST", hostname)
        from config_ini_persist import write_config_ini

        if not write_config_ini(config, config_path):
            return _setup_result(
                "FAIL — could not write config.ini (check ownership; must not be root:root)",
                ok=False,
            )

        reload_config()
        return _setup_result(
            f"Saved progressives_source={progressives_url}"
            + (f" topbox_host={hostname}" if hostname else "")
            + " — restart DyzineFrame if progressives do not update immediately.",
            ok=True,
        )


def _html_escape(text: str) -> str:
    return (
        text.replace("&", "&amp;")
        .replace("<", "&lt;")
        .replace(">", "&gt;")
        .replace('"', "&quot;")
    )


def _setup_result(message: str, *, ok: bool) -> str:
    color = "#1a7f37" if ok else "#cf222e"
    return f"""<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>OHD Setup</title></head>
<body style="font-family:sans-serif;max-width:720px;margin:2rem auto;">
<p style="color:{color};">{_html_escape(message)}</p>
<p><a href="/ohd/setup">Back to Topbox picker</a></p>
</body></html>"""


_OHD_SETUP_HTML = """<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>OHD Topbox Setup</title>
  <style>
    body {{ font-family: sans-serif; max-width: 820px; margin: 2rem auto; }}
    table {{ border-collapse: collapse; width: 100%; margin-top: 1rem; }}
    th, td {{ border: 1px solid #ccc; padding: 0.5rem; text-align: left; }}
    tr.master {{ background: #eef9ee; }}
    .muted {{ color: #666; font-size: 0.9rem; }}
    button {{ margin-top: 1rem; padding: 0.5rem 1rem; }}
  </style>
</head>
<body>
  <h1>OHD — Select Topbox Pi</h1>
  <p class="muted">Scans WiFi/LAN for DyzineFrame devices whose hostname starts with <code>topbox</code>.
  Master units (role <code>M</code> or hostname ending in <code>M</code>) are listed first.</p>
  <p><strong>Current:</strong> {current_host or "(none)"}<br>
  <span class="muted">{current_source or "(progressives_source unset)"}</span></p>
  <button type="button" id="scanBtn">Scan network</button>
  <span id="status" class="muted"></span>
  <form id="pickForm" action="/ohd/setup" method="POST">
    <input type="hidden" name="ip" id="pickIp" value="">
    <input type="hidden" name="hostname" id="pickHost" value="">
    <input type="hidden" name="progressives_url" id="pickUrl" value="">
    <table id="devTable" style="display:none;">
      <thead>
        <tr><th></th><th>Hostname</th><th>Role</th><th>Asset</th><th>IP</th><th>Mode</th></tr>
      </thead>
      <tbody id="devBody"></tbody>
    </table>
    <button type="submit" id="saveBtn" style="display:none;">Use selected Topbox</button>
  </form>
  <script>
    const scanBtn = document.getElementById('scanBtn');
    const statusEl = document.getElementById('status');
    const devTable = document.getElementById('devTable');
    const devBody = document.getElementById('devBody');
    const saveBtn = document.getElementById('saveBtn');
    const pickIp = document.getElementById('pickIp');
    const pickHost = document.getElementById('pickHost');
    const pickUrl = document.getElementById('pickUrl');

    scanBtn.addEventListener('click', async () => {{
      statusEl.textContent = 'Scanning…';
      devBody.innerHTML = '';
      devTable.style.display = 'none';
      saveBtn.style.display = 'none';
      try {{
        const resp = await fetch('/ohd/api/topbox-scan');
        const data = await resp.json();
        const devices = data.devices || [];
        if (!devices.length) {{
          statusEl.textContent = 'No topbox devices found on ' + (data.scanned_subnets || []).join(', ');
          return;
        }}
        statusEl.textContent = 'Found ' + devices.length + ' device(s) on ' + (data.scanned_subnets || []).join(', ');
        devices.forEach((d, idx) => {{
          const tr = document.createElement('tr');
          if ((d.machine_role || '').toUpperCase() === 'M' || (d.hostname || '').toLowerCase().endsWith('m')) {{
            tr.className = 'master';
          }}
          tr.innerHTML =
            '<td><input type="radio" name="pick" value="' + idx + '"' + (idx === 0 ? ' checked' : '') + '></td>' +
            '<td>' + (d.hostname || '') + '</td>' +
            '<td>' + (d.machine_role || '—') + '</td>' +
            '<td>' + (d.asset_number != null ? d.asset_number : '—') + '</td>' +
            '<td>' + (d.ip || '') + '</td>' +
            '<td>' + (d.display_mode || '') + '</td>';
          tr.dataset.ip = d.ip || '';
          tr.dataset.host = d.hostname || '';
          tr.dataset.url = d.progressives_url || '';
          devBody.appendChild(tr);
        }});
        devTable.style.display = '';
        saveBtn.style.display = '';
        syncPick();
      }} catch (err) {{
        statusEl.textContent = 'Scan failed: ' + err;
      }}
    }});

    function syncPick() {{
      const selected = document.querySelector('input[name="pick"]:checked');
      if (!selected) return;
      const tr = selected.closest('tr');
      pickIp.value = tr.dataset.ip || '';
      pickHost.value = tr.dataset.host || '';
      pickUrl.value = tr.dataset.url || '';
    }}

    devBody.addEventListener('change', syncPick);
    document.getElementById('pickForm').addEventListener('submit', syncPick);
  </script>
</body>
</html>"""
