import { useCallback, useEffect, useRef, useState } from "react";
import QRCode from "qrcode";
import { SimplePool, generateSecretKey, getPublicKey } from "nostr-tools";
import { BunkerSigner, createNostrConnectURI } from "nostr-tools/nip46";
import { useToast } from "../contexts/ToastContext";
import type { Nip98Signer } from "../api";

const DEFAULT_RELAYS = [
  "wss://relay.damus.io",
  "wss://relay.nostr.band",
];
const WAIT_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes

function randomHex(bytes: number): string {
  const arr = new Uint8Array(bytes);
  crypto.getRandomValues(arr);
  return Array.from(arr)
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
}

interface RemoteSignerQRProps {
  onConnect: (sign: Nip98Signer) => Promise<void>;
  onError: (message: string | null) => void;
  disabled?: boolean;
  /** When true, show "Signing in…" but keep mounted so the signer pool stays alive. */
  signingIn?: boolean;
}

export function RemoteSignerQR({ onConnect, onError, disabled, signingIn }: RemoteSignerQRProps) {
  const { showToast } = useToast();
  const [connectionUri, setConnectionUri] = useState<string | null>(null);
  const [qrDataUrl, setQrDataUrl] = useState<string | null>(null);
  const [copyLabel, setCopyLabel] = useState("Copy");
  const [waiting, setWaiting] = useState(false);
  const poolRef = useRef<SimplePool | null>(null);
  const abortRef = useRef<AbortController | null>(null);
  const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  const clearTimeoutRef = useCallback(() => {
    if (timeoutRef.current) {
      clearTimeout(timeoutRef.current);
      timeoutRef.current = null;
    }
  }, []);

  const generateUri = useCallback(() => {
    const clientSecret = generateSecretKey();
    const clientPubkey = getPublicKey(clientSecret);
    const secret = randomHex(32);
    const uri = createNostrConnectURI({
      clientPubkey,
      relays: DEFAULT_RELAYS,
      secret,
      name: "Sats Faucet",
      url: typeof window !== "undefined" ? window.location.origin : "",
    });
    return { uri, clientSecret };
  }, []);

  const startWaiting = useCallback(
    (uri: string, clientSecret: Uint8Array) => {
      clearTimeoutRef();
      const pool = new SimplePool();
      poolRef.current = pool;
      const abort = new AbortController();
      abortRef.current = abort;
      setWaiting(true);

      timeoutRef.current = setTimeout(() => {
        timeoutRef.current = null;
        if (abortRef.current?.signal.aborted) return;
        abortRef.current?.abort();
        onError("Connection timed out. Try regenerating the QR code.");
        setWaiting(false);
      }, WAIT_TIMEOUT_MS);

      BunkerSigner.fromURI(clientSecret, uri, { pool }, abort.signal)
        .then(async (signer) => {
          if (abortRef.current?.signal.aborted) return;
          clearTimeoutRef();
          setWaiting(false);
          const sign: Nip98Signer = (e) => signer.signEvent(e);
          try {
            await onConnect(sign);
          } catch (err) {
            const msg = err instanceof Error ? err.message : String(err);
            onError(msg);
          }
        })
        .catch((err) => {
          if (abortRef.current?.signal.aborted) return;
          clearTimeoutRef();
          setWaiting(false);
          const msg = err instanceof Error ? err.message : String(err);
          onError(msg);
        });
    },
    [onConnect, onError, clearTimeoutRef]
  );

  const regenerate = useCallback(() => {
    clearTimeoutRef();
    abortRef.current?.abort();
    abortRef.current = null;
    if (poolRef.current) {
      poolRef.current.destroy();
      poolRef.current = null;
    }
    onError(null);
    const { uri, clientSecret } = generateUri();
    setConnectionUri(uri);
    QRCode.toDataURL(uri, { width: 220, margin: 1 }).then(setQrDataUrl).catch(() => setQrDataUrl(null));
    startWaiting(uri, clientSecret);
  }, [generateUri, startWaiting, onError, clearTimeoutRef]);

  useEffect(() => {
    if (disabled) return;
    const { uri, clientSecret } = generateUri();
    setConnectionUri(uri);
    setQrDataUrl(null);
    QRCode.toDataURL(uri, { width: 220, margin: 1 }).then(setQrDataUrl).catch(() => setQrDataUrl(null));
    startWaiting(uri, clientSecret);
    return () => {
      clearTimeoutRef();
      abortRef.current?.abort();
      if (poolRef.current) {
        poolRef.current.destroy();
        poolRef.current = null;
      }
    };
  }, [disabled, generateUri, startWaiting, clearTimeoutRef]);

  const handleCopy = useCallback(() => {
    if (!connectionUri) return;
    navigator.clipboard.writeText(connectionUri).then(() => {
      setCopyLabel("Copied");
      showToast("Copied");
      setTimeout(() => setCopyLabel("Copy"), 1500);
    });
  }, [connectionUri, showToast]);

  if (disabled) return null;

  return (
    <div className="remote-signer-qr-content">
      <div className="remote-signer-qr-card">
        {qrDataUrl ? (
          <img src={qrDataUrl} alt="Scan to connect with your signer" />
        ) : (
          <span className="remote-signer-qr-placeholder">Generating QR…</span>
        )}
      </div>
      {signingIn ? (
        <p className="remote-signer-waiting" role="status">
          Signing in…
        </p>
      ) : waiting ? (
        <p className="remote-signer-waiting" role="status">
          Waiting for signer approval…
        </p>
      ) : null}
      <div className="remote-signer-connection-row">
        <input
          type="text"
          readOnly
          value={connectionUri ?? ""}
          aria-label="Connection string"
        />
        <button
          type="button"
          className="remote-signer-copy-btn"
          onClick={handleCopy}
          disabled={!connectionUri}
          aria-label="Copy connection string"
        >
          {copyLabel}
        </button>
      </div>
      <button type="button" className="remote-signer-regenerate-btn" onClick={regenerate}>
        Regenerate QR
      </button>
    </div>
  );
}
