/**
 * `<RecoveryApprove />` — guardian-side widget for V3.10 social key
 * recovery.
 *
 * Renders one approval card per pending recovery request. Each card
 * shows:
 *   - Who is asking (the requester's Shade address).
 *   - The original (lost) device's setup-time fingerprint.
 *   - The new device's TEMPORARY fingerprint — the value the user
 *     MUST verify OOB before approving.
 *
 * The widget enforces V3.10 acceptance criterion #3: an approval is
 * blocked behind a "fingerprint matches" checkbox AND an "I have
 * verified OOB" checkbox before the green button is even clickable.
 *
 * The widget does NOT call `attachGuardian` itself — instead, the host
 * application calls `attachGuardian({ approve: ... })` and forwards
 * the approval into a queue this component consumes via the
 * `pending` + `onResolve` props. That way the same approve channel
 * can be shared between this UI and any out-of-app surface (push
 * notification, OS-level prompt, hardware confirmation, etc.).
 */

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

export interface PendingApproval {
  /** Stable id used to correlate approve/decline with the deferred promise. */
  id: string;
  /** Recovery context handed to the approve handler. */
  ctx: GuardianApproveContext;
}

export interface RecoveryApproveProps {
  /** Pending requests waiting on the user. Caller updates this list. */
  pending: ReadonlyArray<PendingApproval>;
  /**
   * Called when the user picks a side. The host wires this to resolve
   * the deferred promise the `attachGuardian` approve callback is
   * blocked on.
   */
  onResolve: (id: string, decision: boolean) => void;
}

export function RecoveryApprove(props: RecoveryApproveProps): React.ReactElement {
  const theme = useShadeRuntimeTheme();
  if (props.pending.length === 0) {
    return (
      <div
        style={{
          padding: 12,
          borderRadius: theme.radius,
          border: `1px dashed ${theme.border}`,
          color: theme.textMuted,
          fontSize: 12,
          fontFamily: theme.font,
          textAlign: 'center',
        }}
      >
        No pending recovery requests.
      </div>
    );
  }
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
      {props.pending.map((p) => (
        <ApprovalCard key={p.id} approval={p} onResolve={props.onResolve} />
      ))}
    </div>
  );
}

function ApprovalCard(props: {
  approval: PendingApproval;
  onResolve: (id: string, decision: boolean) => void;
}): React.ReactElement {
  const theme = useShadeRuntimeTheme();
  const { ctx } = props.approval;
  const [matches, setMatches] = useState(false);
  const [confirmedOob, setConfirmedOob] = useState(false);

  const requestedAt = useMemo(() => new Date(ctx.requestReceivedAt).toISOString(), [ctx]);
  const setupAt = useMemo(() => new Date(ctx.depositCreatedAt).toISOString(), [ctx]);

  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: 4 }}>
        Recovery request for {ctx.originalAddress}
      </div>
      <div style={{ fontSize: 11, color: theme.textMuted, marginBottom: 10 }}>
        Requester: <span style={{ fontFamily: theme.fontMono }}>{ctx.requesterAddress}</span>
        <br />
        Original deposit: {setupAt}, request received: {requestedAt}
      </div>

      <div style={{ marginBottom: 10 }}>
        <div style={{ fontSize: 11, color: theme.textMuted, marginBottom: 4 }}>
          Original device fingerprint (at setup):
        </div>
        <pre
          style={{
            margin: 0,
            padding: 10,
            borderRadius: 6,
            background: theme.bg,
            border: `1px solid ${theme.border}`,
            fontFamily: theme.fontMono,
            fontSize: 12,
            whiteSpace: 'pre-wrap',
            wordBreak: 'break-all',
          }}
        >
          {ctx.setupFingerprint}
        </pre>
      </div>

      <div style={{ marginBottom: 10 }}>
        <div style={{ fontSize: 11, color: theme.textMuted, marginBottom: 4 }}>
          New device fingerprint (verify OOB before approving):
        </div>
        <pre
          style={{
            margin: 0,
            padding: 10,
            borderRadius: 6,
            background: theme.bg,
            border: `1px solid ${theme.accent}`,
            fontFamily: theme.fontMono,
            fontSize: 12,
            whiteSpace: 'pre-wrap',
            wordBreak: 'break-all',
          }}
        >
          {ctx.requesterFingerprint}
        </pre>
      </div>

      <label style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, marginBottom: 6 }}>
        <input
          type="checkbox"
          checked={matches}
          onChange={(e) => setMatches(e.target.checked)}
        />
        I confirm the new device's fingerprint matches what the requester read aloud.
      </label>
      <label style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, marginBottom: 12 }}>
        <input
          type="checkbox"
          checked={confirmedOob}
          onChange={(e) => setConfirmedOob(e.target.checked)}
        />
        I verified the requester's identity over a side channel (call, in person), not Shade itself.
      </label>

      <div style={{ display: 'flex', gap: 8 }}>
        <button
          type="button"
          onClick={() => props.onResolve(props.approval.id, false)}
          style={{
            flex: 1,
            background: 'transparent',
            color: theme.text,
            border: `1px solid ${theme.border}`,
            borderRadius: 6,
            padding: '8px 14px',
            fontSize: 12,
            fontWeight: 600,
            cursor: 'pointer',
          }}
        >
          Decline
        </button>
        <button
          type="button"
          disabled={!matches || !confirmedOob}
          onClick={() => props.onResolve(props.approval.id, true)}
          style={{
            flex: 1,
            background: matches && confirmedOob ? theme.accent : 'transparent',
            color: matches && confirmedOob ? theme.bg : theme.textMuted,
            border: `1px solid ${matches && confirmedOob ? theme.accent : theme.border}`,
            borderRadius: 6,
            padding: '8px 14px',
            fontSize: 12,
            fontWeight: 600,
            cursor: matches && confirmedOob ? 'pointer' : 'not-allowed',
          }}
        >
          Release share
        </button>
      </div>
    </div>
  );
}

/**
 * Tiny helper that turns the `attachGuardian({ approve })` callback
 * shape into a deferred queue the `<RecoveryApprove />` widget can
 * consume. Returns a tuple of:
 *
 *   - `approve(ctx)`: the function to pass to `attachGuardian`.
 *   - `pending`: live list of pending approvals (consumer is expected
 *     to mirror this into React state via subscribe()).
 *   - `subscribe(listener)`: register for change notifications.
 *   - `resolve(id, decision)`: the value the widget calls.
 */
export function createApprovalQueue(): {
  approve: (ctx: GuardianApproveContext) => Promise<boolean>;
  pending: () => PendingApproval[];
  subscribe: (listener: () => void) => () => void;
  resolve: (id: string, decision: boolean) => void;
} {
  const queue: Array<{
    id: string;
    ctx: GuardianApproveContext;
    resolve: (decision: boolean) => void;
  }> = [];
  const listeners = new Set<() => void>();
  const notify = (): void => {
    for (const l of listeners) l();
  };

  return {
    approve: (ctx) =>
      new Promise<boolean>((resolveDecision) => {
        const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
        queue.push({ id, ctx, resolve: resolveDecision });
        notify();
      }),
    pending: () => queue.map((q) => ({ id: q.id, ctx: q.ctx })),
    subscribe: (listener) => {
      listeners.add(listener);
      return () => listeners.delete(listener);
    },
    resolve: (id, decision) => {
      const idx = queue.findIndex((q) => q.id === id);
      if (idx === -1) return;
      const [entry] = queue.splice(idx, 1);
      entry!.resolve(decision);
      notify();
    },
  };
}
