/**
 * The generated pairing code panel (ticket #77, lot F2).
 *
 * **Grouping is presentation only.** The code is rendered as two blocks of four
 * for readability, but the blocks are separate elements with CSS spacing — there
 * is no separator character anywhere in the value, and `Copier` puts the exact
 * canonical code (`AB12CD34`) on the clipboard. A dash rendered here would be
 * retyped by the user and would reintroduce the very failure #75 just fixed
 * (arbitrage Main, carnet #77).
 *
 * The countdown is honest about a code that is already dead: at expiry the panel
 * switches to the expired state rather than letting someone type a code the
 * server will refuse.
 */

import { useEffect, useState } from "react";

import type { PairingCode } from "@/domain";
import { Button, Panel } from "@/shared";
import { formatExpiresIn } from "./formatActivity";

interface PairingCodePanelProps {
  code: PairingCode;
  onRegenerate: () => void;
  onDismiss: () => void;
}

/** Splits the code into fixed blocks of 4 for display. Never mutates the value. */
export function codeBlocks(code: string, size = 4): string[] {
  const blocks: string[] = [];
  for (let i = 0; i < code.length; i += size) blocks.push(code.slice(i, i + size));
  return blocks;
}

/** Copies text, preferring the async clipboard API and degrading silently. */
async function copyToClipboard(text: string): Promise<boolean> {
  try {
    if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
      await navigator.clipboard.writeText(text);
      return true;
    }
  } catch {
    // Denied permission / insecure context: fall through to the failure notice.
  }
  return false;
}

export function PairingCodePanel({ code, onRegenerate, onDismiss }: PairingCodePanelProps) {
  const [remaining, setRemaining] = useState(() => formatExpiresIn(code.expiresAtMs));
  const [copied, setCopied] = useState(false);

  useEffect(() => {
    setRemaining(formatExpiresIn(code.expiresAtMs));
    const timer = setInterval(() => setRemaining(formatExpiresIn(code.expiresAtMs)), 1000);
    return () => clearInterval(timer);
  }, [code.expiresAtMs]);

  useEffect(() => {
    setCopied(false);
  }, [code.code]);

  useEffect(() => {
    if (!copied) return;
    const timer = setTimeout(() => setCopied(false), 2000);
    return () => clearTimeout(timer);
  }, [copied]);

  const expired = remaining === null;

  return (
    <Panel data-testid="pairing-code-panel" className="flex flex-col gap-3">
      {expired ? (
        <>
          <p className="text-sm text-muted">Ce code a expiré.</p>
          <div>
            <Button size="sm" onClick={onRegenerate}>
              Générer un nouveau code
            </Button>
          </div>
        </>
      ) : (
        <>
          <p className="text-sm text-muted">Saisissez ce code sur le nouvel appareil.</p>
          <p
            data-testid="pairing-code-value"
            // The accessible name is the plain code: a screen reader must hear
            // the value to type, not the visual blocks.
            aria-label={code.code}
            className="flex gap-3 font-mono text-2xl font-semibold tracking-[0.2em] text-content sm:text-3xl"
          >
            {codeBlocks(code.code).map((block, i) => (
              <span key={i} aria-hidden="true">
                {block}
              </span>
            ))}
          </p>
          <div className="flex flex-wrap items-center gap-2">
            <Button
              size="sm"
              variant="ghost"
              onClick={() =>
                void copyToClipboard(code.code).then((ok) => setCopied(ok))
              }
            >
              {copied ? "Copié" : "Copier"}
            </Button>
            <span className="text-xs text-faint" data-testid="pairing-code-countdown">
              {remaining}
            </span>
            <Button size="sm" variant="ghost" className="ml-auto" onClick={onDismiss}>
              Fermer
            </Button>
          </div>
        </>
      )}
    </Panel>
  );
}
