import { useEffect, useRef, useState } from "react";
import { ApiError } from "../../api/client";
import type { SupportCode } from "../../api/types";
import { Button } from "../../components/ui/Button";
import { Modal } from "../../components/ui/Modal";
import { Spinner } from "../../components/ui/Spinner";
import { ErrorState } from "../../components/ui/States";
import { CopyIcon } from "../../components/layout/icons";
import { useClipboard } from "../../lib/useClipboard";
import { useGenerateCode } from "./hooks";
import "./codes.css";

interface GenerateCodeModalProps {
  /** Whether the generate dialog is open. */
  open: boolean;
  /** Operator name to attribute the code to (server stamps `created_by`). */
  technicianName?: string;
  onClose: () => void;
}

/**
 * Generate-a-code flow. Opening the dialog mints a fresh code immediately, then
 * reveals it large in JetBrains Mono so the tech can read it to the end user
 * over the phone. The code is the single high-signal element on this surface;
 * everything else is secondary. There is no per-second countdown — the
 * SupportCode the API returns has no `expires_at`, and a redeem/cancel surfaces
 * through the table's poll, so a timer here would be both impossible to source
 * accurately and a needless render storm.
 */
export function GenerateCodeModal({
  open,
  technicianName,
  onClose,
}: GenerateCodeModalProps) {
  const generate = useGenerateCode();
  const { copied, copy } = useClipboard();
  const [result, setResult] = useState<SupportCode | null>(null);
  // Minting a code is a durable single-use side effect. Guard it behind a ref so
  // StrictMode's mount->cleanup->mount double-invoke can't fire two real POSTs
  // per open; re-arm on close so the next open mints fresh.
  const mintedFor = useRef(false);

  // Mint once when the dialog opens; reset on close so a re-open mints a fresh
  // code. Minting in an effect (not on a button click) lets the dialog own the
  // loading/error/success states cleanly, mirroring JoinSessionModal.
  useEffect(() => {
    if (!open) {
      setResult(null);
      generate.reset();
      mintedFor.current = false; // re-arm for the next open
      return;
    }
    if (mintedFor.current) return; // StrictMode remount: already minted
    mintedFor.current = true;
    let cancelled = false;
    generate
      .mutateAsync({ technician_name: technicianName })
      .then((res) => {
        if (!cancelled) setResult(res);
      })
      .catch(() => {
        // Surfaced via generate.isError below.
      });
    return () => {
      cancelled = true;
    };
    // Mint exactly once per open. The mutation object is stable.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open]);

  if (!open) return null;

  return (
    <Modal
      open={open}
      title="Support code"
      onClose={onClose}
      footer={
        <Button variant="primary" onClick={onClose}>
          Done
        </Button>
      }
    >
      {generate.isPending && !result ? (
        <div className="codegen__loading">
          <Spinner label="Generating code…" />
        </div>
      ) : generate.isError ? (
        <ErrorState
          title="Could not generate a code"
          message={
            generate.error instanceof ApiError
              ? generate.error.message
              : "The relay did not return a code. Check the relay status, then try again."
          }
          action={
            <Button
              variant="primary"
              onClick={() =>
                void generate
                  .mutateAsync({ technician_name: technicianName })
                  .then(setResult)
                  .catch(() => {})
              }
            >
              Try again
            </Button>
          }
        />
      ) : result ? (
        <>
          <p className="codegen__lede">
            Read this code to the end user. It starts an attended support session
            and can be used once.
          </p>

          <div className="codegen__codewrap">
            <output className="codegen__code" aria-label={`Support code ${spell(result.code)}`}>
              {result.code}
            </output>
            <Button
              variant="ghost"
              size="sm"
              onClick={() => void copy(result.code)}
              aria-label={copied ? "Code copied to clipboard" : "Copy code to clipboard"}
            >
              <CopyIcon width={14} height={14} />
              {copied ? "Copied" : "Copy"}
            </Button>
          </div>

          <p className="codegen__hint">
            It stays active until the user redeems it or you cancel it. Once
            redeemed it cannot be used again.
          </p>
        </>
      ) : null}
    </Modal>
  );
}

/**
 * Spell a grouped code out for the screen-reader label so it is announced
 * character by character ("K, 7, P, ...") instead of as a mangled word. The
 * visible code stays the compact `XXX-XXX-XXX` form.
 */
function spell(code: string): string {
  return code
    .replace(/-/g, " ")
    .split("")
    .filter((c) => c !== " ")
    .join(" ");
}
