/**
 * `<RecoverySetup />` — primary-device widget for V3.10 social key
 * recovery setup.
 *
 * Lets the user pick guardians, choose a threshold (k-of-n), and run
 * `setupRecovery` from `@shade/recovery`. The component is fully
 * controlled — guardian addresses come from a prop the consumer
 * populates from their address book; the widget never tries to read
 * the host app's contact list directly.
 *
 * After a successful run, the widget displays the `setupId` and the
 * setup-time fingerprint with a "Copy recovery card" action so the
 * user can record the data they'll need to retrieve their identity
 * later (setupId, threshold, guardian addresses, original
 * fingerprint).
 */

import React, { useMemo, useState } from 'react';
import {
  setupRecovery,
  type RecoveryDeliver,
  type SetupRecoveryResult,
} from '@shade/recovery';
import { useShadeRuntime, useShadeRuntimeTheme } from '../../ShadeRuntimeProvider.js';

export interface RecoverySetupProps {
  /**
   * Candidate guardian directory. The widget shows them as toggleable
   * chips. Consumer is expected to source these from the host app's
   * existing peer / contacts list.
   */
  candidates: ReadonlyArray<{ address: string; label?: string }>;
  /**
   * Outbound transport callback — same shape as `setupRecovery({
   * deliver })`. Wires through the host app's message-delivery layer.
   */
  deliver: RecoveryDeliver;
  /**
   * Optional list of peer addresses whose Double-Ratchet sessions
   * should be embedded in the backup blob. Defaults to `[]` (identity +
   * prekeys only, no sessions).
   */
  knownAddresses?: ReadonlyArray<string>;
  /**
   * Default threshold proposed in the slider. Defaults to
   * `Math.ceil(candidates.length / 2)`.
   */
  defaultThreshold?: number;
  /** Fired after a successful setup completes. */
  onComplete?: (result: SetupRecoveryResult) => void;
  /** Fired if setup throws. */
  onError?: (err: Error) => void;
}

/**
 * Primary-device widget. Use inside `<ShadeRuntimeProvider runtime>`.
 */
export function RecoverySetup(props: RecoverySetupProps): React.ReactElement {
  const runtime = useShadeRuntime();
  const theme = useShadeRuntimeTheme();
  const candidates = useMemo(() => [...props.candidates], [props.candidates]);
  const [selected, setSelected] = useState<Set<string>>(() => new Set());
  const [threshold, setThreshold] = useState<number>(
    () => props.defaultThreshold ?? Math.ceil(Math.max(1, candidates.length / 2)),
  );
  const [busy, setBusy] = useState(false);
  const [result, setResult] = useState<SetupRecoveryResult | null>(null);
  const [error, setError] = useState<string | null>(null);

  const guardianList = candidates.filter((c) => selected.has(c.address)).map((c) => c.address);
  const validThreshold = threshold >= 1 && threshold <= guardianList.length;
  const canRun = !busy && guardianList.length >= 1 && validThreshold;

  const toggle = (addr: string): void => {
    setSelected((prev) => {
      const next = new Set(prev);
      if (next.has(addr)) next.delete(addr);
      else next.add(addr);
      return next;
    });
  };

  const run = async (): Promise<void> => {
    if (!canRun) return;
    setBusy(true);
    setError(null);
    setResult(null);
    try {
      const r = await setupRecovery({
        shade: runtime,
        guardians: guardianList,
        threshold,
        deliver: props.deliver,
        knownAddresses: [...(props.knownAddresses ?? [])],
      });
      setResult(r);
      props.onComplete?.(r);
    } catch (err) {
      const message = (err as Error).message;
      setError(message);
      props.onError?.(err as Error);
    } finally {
      setBusy(false);
    }
  };

  const copyCard = async (): Promise<void> => {
    if (result === null) return;
    const text = formatRecoveryCard(runtime.myAddress, result, guardianList);
    if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText !== undefined) {
      await navigator.clipboard.writeText(text);
    }
  };

  return (
    <div
      style={{
        background: theme.bgElevated,
        border: `1px solid ${theme.border}`,
        borderRadius: theme.radius,
        padding: 16,
        color: theme.text,
        fontFamily: theme.font,
      }}
    >
      <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 8 }}>
        Set up social key recovery
      </div>
      <div style={{ fontSize: 12, color: theme.textMuted, marginBottom: 12 }}>
        Pick guardians (people who can help you recover this identity if you lose your device)
        and a threshold. Any threshold-many guardians together can restore — fewer cannot.
      </div>

      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 12 }}>
        {candidates.map((c) => {
          const on = selected.has(c.address);
          return (
            <button
              key={c.address}
              type="button"
              onClick={() => toggle(c.address)}
              style={{
                padding: '6px 10px',
                borderRadius: 6,
                fontSize: 12,
                fontFamily: theme.fontMono,
                border: `1px solid ${on ? theme.accent : theme.border}`,
                background: on ? theme.accent : 'transparent',
                color: on ? theme.bg : theme.text,
                cursor: 'pointer',
              }}
            >
              {c.label ?? c.address}
            </button>
          );
        })}
      </div>

      <div style={{ marginBottom: 12 }}>
        <label style={{ fontSize: 12, color: theme.textMuted, display: 'block', marginBottom: 4 }}>
          Threshold (k-of-{Math.max(1, guardianList.length)}): {threshold}
        </label>
        <input
          type="range"
          min={1}
          max={Math.max(1, guardianList.length)}
          step={1}
          value={Math.min(threshold, Math.max(1, guardianList.length))}
          onChange={(e) => setThreshold(parseInt(e.target.value, 10))}
          style={{ width: '100%' }}
        />
      </div>

      <button
        type="button"
        disabled={!canRun}
        onClick={() => void run()}
        style={{
          background: theme.accent,
          color: theme.bg,
          border: `1px solid ${theme.accent}`,
          borderRadius: 6,
          padding: '8px 14px',
          fontFamily: theme.font,
          fontSize: 12,
          fontWeight: 600,
          cursor: canRun ? 'pointer' : 'not-allowed',
          opacity: canRun ? 1 : 0.5,
        }}
      >
        {busy ? 'Distributing shares…' : `Distribute ${threshold}-of-${guardianList.length}`}
      </button>

      {error !== null && (
        <div
          style={{
            marginTop: 12,
            padding: 10,
            borderRadius: 6,
            border: `1px solid ${theme.danger}`,
            background: 'rgba(239, 68, 68, 0.08)',
            color: theme.danger,
            fontSize: 12,
          }}
        >
          {error}
        </div>
      )}

      {result !== null && (
        <div
          style={{
            marginTop: 12,
            padding: 10,
            borderRadius: 6,
            border: `1px solid ${theme.success}`,
            background: 'rgba(34, 197, 94, 0.08)',
            color: theme.text,
            fontSize: 12,
            display: 'flex',
            flexDirection: 'column',
            gap: 6,
          }}
        >
          <div style={{ fontWeight: 600, color: theme.success }}>
            ✓ Distributed to {result.deliveries.filter((d) => d.error === null).length} of {result.guardianCount}
          </div>
          <div>
            setupId: <span style={{ fontFamily: theme.fontMono }}>{result.setupId}</span>
          </div>
          <div style={{ fontSize: 11 }}>
            Save this id (or click "Copy recovery card") together with the guardian list. You'll
            need both on the new device.
          </div>
          <button
            type="button"
            onClick={() => void copyCard()}
            style={{
              alignSelf: 'flex-start',
              background: 'transparent',
              color: theme.text,
              border: `1px solid ${theme.border}`,
              borderRadius: 6,
              padding: '6px 10px',
              fontSize: 11,
              cursor: 'pointer',
            }}
          >
            Copy recovery card
          </button>
        </div>
      )}
    </div>
  );
}

/**
 * Build the human-readable "recovery card" the user should record on
 * paper / a second device. Plain text on purpose — no QR, no JSON —
 * so the user can transcribe it manually if needed.
 */
export function formatRecoveryCard(
  ownAddress: string,
  result: SetupRecoveryResult,
  guardians: ReadonlyArray<string>,
): string {
  const lines: string[] = [];
  lines.push('Shade recovery card');
  lines.push('=====================');
  lines.push(`Owner: ${ownAddress}`);
  lines.push(`Threshold: ${result.threshold} of ${result.guardianCount}`);
  lines.push(`SetupId: ${result.setupId}`);
  lines.push(`Setup fingerprint: ${result.setupFingerprint}`);
  lines.push('Guardians:');
  for (const g of guardians) lines.push(`  - ${g}`);
  lines.push('');
  lines.push('Keep this card somewhere safe but accessible. You will need ALL of these');
  lines.push('values on a new device to begin recovery.');
  return lines.join('\n');
}
