import React, { useCallback, useEffect, useState } from 'react';
import type { Shade } from '@shade/sdk';
import { useShadeRuntime, useShadeRuntimeTheme } from '../ShadeRuntimeProvider.js';
import type { ShadeTheme } from '../theme.js';

export interface FingerprintGateProps {
  /** The peer address that the gated UI talks to. */
  peerAddress: string;
  /**
   * Optional render-prop that replaces the default "verify first" UI.
   * Receives the peer's safety number and a callback that marks the peer
   * verified (after the user confirmed OOB).
   */
  fallback?: (props: FingerprintGateFallbackProps) => React.ReactNode;
  /** Called when the peer transitions from unverified → verified. */
  onVerified?: () => void;
  children: React.ReactNode;
}

export interface FingerprintGateFallbackProps {
  peerAddress: string;
  fingerprint: string;
  /** Mark the peer verified at the current fingerprint. */
  verify: () => Promise<void>;
  /** Copy a copy-pasteable OOB text for the safety number. */
  copyOob: () => Promise<void>;
}

/**
 * `<FingerprintGate />` — blocks rendering of `children` until the
 * `peerAddress` has been verified at its current fingerprint. Pairs with
 * the V3.3 `Shade.beforeFirstLargeFile` / `Shade.beforeBackupImport` /
 * `Shade.beforeNewDeviceTrust` hooks to expose verification in UI flows
 * outside the SDK-driven gates.
 *
 * SSR-safe: the gate fetches state inside `useEffect`, so initial render
 * shows nothing until the runtime resolves verification status.
 */
export function FingerprintGate(props: FingerprintGateProps): React.ReactElement | null {
  const runtime = useShadeRuntime();
  const theme = useShadeRuntimeTheme();
  const status = useGateStatus(runtime, props.peerAddress);

  const verify = useCallback(async () => {
    await runtime.markPeerVerified(props.peerAddress);
    status.refresh();
  }, [runtime, props.peerAddress, status]);

  const copyOob = useCallback(async () => {
    if (status.fingerprint === null) return;
    await copyToClipboard(formatOobText(props.peerAddress, status.fingerprint));
  }, [props.peerAddress, status.fingerprint]);

  useEffect(() => {
    if (status.verified === true) props.onVerified?.();
  }, [status.verified, props.onVerified, props]);

  if (status.verified === null) return null; // SSR / first paint
  if (status.verified === true) return <>{props.children}</>;
  if (status.fingerprint === null) return null; // session not ready yet

  if (props.fallback !== undefined) {
    return (
      <>{props.fallback({
        peerAddress: props.peerAddress,
        fingerprint: status.fingerprint,
        verify,
        copyOob,
      })}</>
    );
  }

  return (
    <DefaultFallback
      peerAddress={props.peerAddress}
      fingerprint={status.fingerprint}
      theme={theme}
      verify={verify}
      copyOob={copyOob}
    />
  );
}

interface GateStatus {
  verified: boolean | null;
  fingerprint: string | null;
  refresh: () => void;
}

function useGateStatus(runtime: Shade, peerAddress: string): GateStatus {
  const [verified, setVerified] = useState<boolean | null>(null);
  const [fingerprint, setFingerprint] = useState<string | null>(null);
  const [tick, setTick] = useState(0);

  useEffect(() => {
    let cancelled = false;
    void (async () => {
      try {
        const fp = await runtime.getFingerprintFor(peerAddress);
        if (cancelled) return;
        setFingerprint(fp);
        const v = await runtime.isPeerVerified(peerAddress);
        if (cancelled) return;
        setVerified(v);
      } catch {
        // No session yet — leave both null so the component renders nothing
        // until a session is established.
        if (cancelled) return;
        setFingerprint(null);
        setVerified(null);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [runtime, peerAddress, tick]);

  const refresh = useCallback(() => setTick((n) => n + 1), []);
  return { verified, fingerprint, refresh };
}

function DefaultFallback(props: {
  peerAddress: string;
  fingerprint: string;
  theme: ShadeTheme;
  verify: () => Promise<void>;
  copyOob: () => Promise<void>;
}): React.ReactElement {
  const { theme } = props;
  return (
    <div
      style={{
        background: theme.bg,
        border: `1px solid ${theme.border}`,
        borderRadius: 8,
        padding: 16,
        color: theme.text,
        fontFamily: theme.font,
      }}
    >
      <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 6 }}>
        Verify {props.peerAddress} before continuing
      </div>
      <div style={{ fontSize: 12, color: theme.textMuted, marginBottom: 12 }}>
        Compare the safety number below over a side channel (call, SMS, in person).
        Approve only after both ends match.
      </div>
      <pre
        style={{
          background: theme.bgElevated,
          border: `1px solid ${theme.border}`,
          borderRadius: 6,
          padding: 12,
          fontFamily: theme.fontMono,
          fontSize: 13,
          color: theme.text,
          whiteSpace: 'pre-wrap',
          wordBreak: 'break-all',
          margin: 0,
        }}
      >
        {props.fingerprint}
      </pre>
      <div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
        <button
          type="button"
          onClick={() => void props.copyOob()}
          style={buttonStyle(theme, 'secondary')}
        >
          Copy OOB text
        </button>
        <button
          type="button"
          onClick={() => void props.verify()}
          style={buttonStyle(theme, 'primary')}
        >
          I have verified
        </button>
      </div>
    </div>
  );
}

function buttonStyle(theme: ShadeTheme, variant: 'primary' | 'secondary'): React.CSSProperties {
  const isPrimary = variant === 'primary';
  return {
    background: isPrimary ? theme.accent : 'transparent',
    color: isPrimary ? theme.bg : theme.text,
    border: `1px solid ${isPrimary ? theme.accent : theme.border}`,
    borderRadius: 6,
    padding: '6px 12px',
    fontFamily: theme.font,
    fontSize: 12,
    fontWeight: 600,
    cursor: 'pointer',
  };
}

/**
 * Build the OOB text that gets copied to the clipboard. Includes the peer
 * address so the recipient knows which conversation it belongs to.
 */
export function formatOobText(peerAddress: string, fingerprint: string): string {
  return `Shade safety number for ${peerAddress}:\n${fingerprint}`;
}

async function copyToClipboard(text: string): Promise<void> {
  if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText !== undefined) {
    await navigator.clipboard.writeText(text);
    return;
  }
  // Headless / SSR: silently no-op.
}
