/**
 * `Settings → Deployment` (ticket #68) — turn IdeA Desktop into a server other
 * devices can reach, without a command line.
 *
 * Pure presentation over {@link useDeployment}; no `invoke()`, and no address is
 * ever derived here — LAN candidates and the upstream URL come from the backend
 * preview (`DesktopServerGateway`).
 *
 * Two UX invariants this screen exists to protect:
 *
 * - **The exposure mode is a decision, not a setting.** It is rendered as radio
 *   *cards* with plain-language consequences, never a technical select, because
 *   choosing wrong (proxy elsewhere, mode "on this computer") fails as a silent
 *   proxy timeout with no IdeA error to read.
 * - **The authorized-proxy field is not a listen address.** That confusion is
 *   the whole reason this screen is worded the way it is; the help text under
 *   the field says so explicitly.
 *
 * This screen no longer shows a pairing code (#77). A code is not a property of
 * a running server — it exists only when someone asks for one — so it lives in
 * the Appareils surface next to the devices it authorises. What is left here is
 * a signpost: starting the server from this screen and finding no way to pair is
 * a dead end.
 */

import { useRef, useState } from "react";

import type { ServerExposureMode } from "@/domain";
import { Button, Field, Input, Panel, cn } from "@/shared";
import { useDeployment } from "./useDeployment";

interface ModeOption {
  mode: ServerExposureMode;
  title: string;
  description: string;
}

/** The three exposure choices, in increasing order of reach. */
const MODE_OPTIONS: ModeOption[] = [
  {
    mode: "localOnly",
    title: "Cet ordinateur uniquement",
    description:
      "Pour utiliser IdeA uniquement sur cet ordinateur. Les appareils distants ne peuvent pas se connecter.",
  },
  {
    mode: "remoteProxyLocal",
    title: "Accès distant, proxy sur cet ordinateur",
    description:
      "À utiliser quand le proxy HTTPS tourne sur la même machine qu'IdeA Desktop.",
  },
  {
    mode: "remoteProxyOtherMachine",
    title: "Accès distant, proxy sur une autre machine",
    description:
      "À utiliser quand le proxy HTTPS tourne sur une autre machine. IdeA n'acceptera que le trafic provenant de ce proxy.",
  },
];

const STATE_LABEL: Record<string, string> = {
  stopped: "Arrêté",
  starting: "Démarrage…",
  running: "En cours d'exécution",
  stopping: "Arrêt…",
  failed: "Échec",
};

/** Copy-to-clipboard button; degrades to disabled where the API is absent. */
function CopyButton({ value, label }: { value: string; label: string }) {
  const [copied, setCopied] = useState(false);
  const supported =
    typeof navigator !== "undefined" && Boolean(navigator.clipboard);
  return (
    <Button
      size="sm"
      variant="ghost"
      aria-label={label}
      disabled={!supported}
      onClick={() => {
        void navigator.clipboard.writeText(value).then(
          () => {
            setCopied(true);
            window.setTimeout(() => setCopied(false), 1500);
          },
          () => setCopied(false),
        );
      }}
    >
      {copied ? "Copié" : "Copier"}
    </Button>
  );
}

/** A read-only value the user is meant to copy elsewhere (never editable). */
function ReadOnlyValue({ value, copyLabel }: { value: string; copyLabel: string }) {
  return (
    <div className="flex items-center gap-2">
      <code className="flex-1 truncate rounded-md border border-border bg-raised px-2 py-1.5 font-mono text-xs text-content">
        {value}
      </code>
      <CopyButton value={value} label={copyLabel} />
    </div>
  );
}

export function DeploymentSettings() {
  const vm = useDeployment();
  // "Modifier le port" (#89) jumps into Accès réseau — imperative focus is the
  // simplest correct answer for a cross-panel affordance on one scrolled page.
  const portInputRef = useRef<HTMLInputElement>(null);

  if (!vm.ready || !vm.settings) {
    return (
      <Panel aria-label="deployment settings" title="Déploiement">
        <p className="text-sm text-muted">Chargement…</p>
      </Panel>
    );
  }

  const { settings, status } = vm;
  const running = status.state === "running";
  const remote = settings.mode !== "localOnly";
  const otherMachine = settings.mode === "remoteProxyOtherMachine";
  const transitioning = status.state === "starting" || status.state === "stopping";

  return (
    <div aria-label="deployment settings" className="flex flex-col gap-4">
      {/* ── Status ────────────────────────────────────────────────────────── */}
      <Panel
        title="Serveur"
        actions={
          running || status.state === "stopping" ? (
            <Button
              size="sm"
              onClick={() => void vm.stop()}
              disabled={vm.busy || transitioning}
            >
              Arrêter
            </Button>
          ) : (
            <Button
              size="sm"
              onClick={() => void vm.start()}
              disabled={vm.busy || transitioning}
            >
              Démarrer
            </Button>
          )
        }
      >
        <div className="flex flex-col gap-2">
          <p className="flex items-center gap-2 text-sm">
            <span
              aria-hidden
              className={cn(
                "size-2 rounded-full",
                running && "bg-success",
                status.state === "failed" && "bg-danger",
                (status.state === "stopped" || transitioning) && "bg-faint",
              )}
            />
            <span className="text-content">{STATE_LABEL[status.state]}</span>
          </p>

          {status.localUrl && (
            <p className="text-xs text-muted">
              URL locale : <code className="font-mono text-content">{status.localUrl}</code>
            </p>
          )}
          {status.publicUrl && (
            <p className="text-xs text-muted">
              URL publique : <code className="font-mono text-content">{status.publicUrl}</code>
            </p>
          )}

          {/* A failure carries the backend's message: it is the correction.
              A port conflict (#89 — most often surfaced by auto-start at
              launch, but shown the same way for a manual start) gets a
              dedicated, actionable message instead of the generic one. */}
          {status.state === "failed" && status.error && (
            status.error.code === "PORT_IN_USE" ? (
              <div className="flex flex-col gap-2">
                <p role="alert" className="text-sm text-danger">
                  Le serveur n'a pas démarré automatiquement. {status.error.message}
                </p>
                <div className="flex gap-2">
                  <Button
                    size="sm"
                    variant="ghost"
                    onClick={() => {
                      portInputRef.current?.scrollIntoView?.({
                        behavior: "smooth",
                        block: "center",
                      });
                      portInputRef.current?.focus();
                    }}
                  >
                    Modifier le port
                  </Button>
                  <Button
                    size="sm"
                    onClick={() => void vm.start()}
                    disabled={vm.busy}
                  >
                    Réessayer
                  </Button>
                </div>
              </div>
            ) : (
              <p role="alert" className="text-sm text-danger">
                {status.error.message}
              </p>
            )
          )}
          {vm.actionError && (
            <p role="alert" className="text-sm text-danger">
              {vm.actionError}
            </p>
          )}

          {/* Auto-start at launch (#89) — a persistence-only toggle, never a
              start trigger; it never runs the server right now. */}
          <label className="flex cursor-pointer items-start gap-2 pt-2">
            <input
              type="checkbox"
              className="mt-0.5"
              aria-label="Démarrer le serveur au lancement d'IdeA"
              checked={settings.autoStart}
              disabled={vm.autoStartBusy}
              onChange={(e) => void vm.setAutoStart(e.target.checked)}
            />
            <span className="flex flex-col gap-0.5">
              <span className="text-sm text-content">
                Démarrer le serveur au lancement d'IdeA
              </span>
              <span className="text-xs text-muted">
                IdeA utilisera les réglages réseau enregistrés ci-dessous au
                prochain démarrage de l'application desktop.
              </span>
            </span>
          </label>
        </div>
      </Panel>

      {/* ── Exposure (ticket #78 — "Accès réseau" per UX) ────────────────── */}
      <Panel title="Accès réseau">
        <div className="flex flex-col gap-4">
          <div
            role="radiogroup"
            aria-label="mode d'accès réseau"
            className="flex flex-col gap-2"
          >
            {MODE_OPTIONS.map((option) => {
              const selected = settings.mode === option.mode;
              return (
                <label
                  key={option.mode}
                  className={cn(
                    "flex cursor-pointer gap-3 rounded-lg border p-3 transition-colors",
                    selected
                      ? "border-border-strong bg-raised"
                      : "border-border hover:bg-raised",
                  )}
                >
                  <input
                    type="radio"
                    name="exposure-mode"
                    className="mt-1"
                    checked={selected}
                    onChange={() => vm.setMode(option.mode)}
                  />
                  <span className="flex flex-col gap-1">
                    <span className="text-sm font-semibold text-content">
                      {option.title}
                    </span>
                    <span className="text-xs text-muted">{option.description}</span>
                  </span>
                </label>
              );
            })}
          </div>

          <Field label="Port">
            {({ id }) => (
              <Input
                ref={portInputRef}
                id={id}
                type="number"
                value={String(settings.port)}
                onChange={(e) => vm.setPort(Number(e.target.value))}
              />
            )}
          </Field>

          {remote && (
            <Field label="Origine publique" hint="Exemple : https://idea.example.com">
              {({ id, describedBy }) => (
                <Input
                  id={id}
                  aria-describedby={describedBy}
                  placeholder="https://idea.example.com"
                  value={settings.publicOrigin ?? ""}
                  onChange={(e) => vm.setPublicOrigin(e.target.value)}
                />
              )}
            </Field>
          )}

          {otherMachine && (
            <>
              {/* Addresses come from the backend probe — never invented here. */}
              <Field
                label="Adresse LAN d'écoute"
                hint="L'adresse de cette machine à laquelle le proxy se connectera."
              >
                {({ id, describedBy }) =>
                  vm.candidateLanAddresses.length > 0 ? (
                    <select
                      id={id}
                      aria-describedby={describedBy}
                      className="rounded-md border border-border bg-surface px-2 py-1.5 text-sm text-content"
                      value={settings.lanBindAddress ?? ""}
                      onChange={(e) => vm.setLanBindAddress(e.target.value)}
                    >
                      <option value="">Sélectionner une adresse…</option>
                      {vm.candidateLanAddresses.map((address) => (
                        <option key={address} value={address}>
                          {address}
                        </option>
                      ))}
                    </select>
                  ) : (
                    <Input
                      id={id}
                      aria-describedby={describedBy}
                      placeholder="192.168.1.42"
                      value={settings.lanBindAddress ?? ""}
                      onChange={(e) => vm.setLanBindAddress(e.target.value)}
                    />
                  )
                }
              </Field>

              <Field
                label="IP/CIDR du proxy autorisé"
                hint="Ce n'est pas l'adresse d'écoute d'IdeA. C'est la machine autorisée à contacter IdeA."
              >
                {({ id, describedBy }) => (
                  <Input
                    id={id}
                    aria-describedby={describedBy}
                    placeholder="203.0.113.7 or 203.0.113.0/24"
                    value={settings.trustedProxies.join(", ")}
                    onChange={(e) => vm.setTrustedProxies(e.target.value)}
                  />
                )}
              </Field>

              <p className="rounded-md border border-warning/40 bg-warning/10 px-3 py-2 text-xs text-warning">
                Si votre proxy n'est pas sur cet ordinateur, choisissez ce mode.
                Sinon, le proxy peut expirer sans erreur IdeA.
              </p>
            </>
          )}

          {/* Warnings inform; they never block a start. */}
          {vm.warnings.map((warning) => (
            <p key={warning.code} className="text-xs text-warning">
              {warning.message}
            </p>
          ))}

          {vm.validationError && (
            <p role="alert" className="text-sm text-danger">
              {vm.validationError}
            </p>
          )}
        </div>
      </Panel>

      {/* ── Proxy setup: the upstream to paste, backend-provided ──────────── */}
      {remote && (
        <Panel title="Configuration du proxy">
          <div className="flex flex-col gap-2">
            <p className="text-xs text-muted">
              Faites pointer votre proxy inverse HTTPS vers cet upstream.
            </p>
            {vm.upstreamUrl ? (
              <ReadOnlyValue value={vm.upstreamUrl} copyLabel="copier l'URL upstream" />
            ) : (
              <p className="text-xs text-faint">
                Complétez les paramètres ci-dessus pour obtenir l'URL upstream.
              </p>
            )}
          </div>
        </Panel>
      )}

      {/* ── Pairing moved to its own surface (#77) — leave a signpost ─────── */}
      <Panel title="Appairage">
        <p className="text-sm text-muted">
          L'appairage est géré dans{" "}
          <span className="text-content">Paramètres → Appareils</span>, où
          vous pouvez générer un code et révoquer des appareils.
        </p>
      </Panel>
    </div>
  );
}
