'use client';
import { ErrorState } from '@/components/ui/feedback/ErrorState';
import { QueryBoundary } from '@platform-modules/ui-primitives';
/**
 * FraudConfigCards — [D] zone of the fraud admin dashboard.
 *
 * One card per fraud adapter (all 13 KNOWN_ADAPTER_KEYS) with:
 *   - enable/disable Switch
 *   - max-action ceiling Select (pass/flag/hold/block or "default" = no ceiling)
 *
 * One economic-policy card:
 *   - disputeWindowDays (30–730)
 *   - reservePct (0–50)
 *   - reserveReleaseDays (0–730)
 *
 * Save pattern: PATCH /api/admin/affiliates/settings with CSRF header.
 * Sends full fraudConfig map every save to avoid partial-replace wipe.
 * Preserves existing adapter.params from the GET response.
 */
import { useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { useT } from '@/lib/i18n/react';
import { fetchWithRefresh } from '@/lib/api/refresh-on-401';
import { getCsrfToken } from '@/lib/csrf';
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from '@/components/ui/overlays/Tooltip';
import { Switch } from '@/components/ui/primitives/Switch/Switch';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/primitives/Select';
import { Button } from '@/components/ui/primitives/Button';
import { NumberInput } from '@/components/ui/primitives/NumberInput';
import { Skeleton } from '@/components/ui/feedback/Skeleton';

type FraudAction = 'pass' | 'flag' | 'hold' | 'block';

interface AdapterOverride {
  enabled: boolean;
  action?: FraudAction;
  params?: Record<string, unknown>;
}

interface SettingsData {
  fraudConfig: Record<string, AdapterOverride>;
  disputeWindowDays: number;
  reservePct: number;
  reserveReleaseDays: number;
}

const ADAPTER_KEYS = [
  'email-canonical',
  'email-disposable',
  'phone-linetype',
  'phone-required-earn',
  'identity-ring',
  'email-catchall-domain',
  'bot-signup',
  'device-fingerprint',
  'ip-cluster',
  'ip-reputation',
  'payment-instrument',
  'velocity-conversion',
  'referral-graph',
] as const;

type AdapterKey = (typeof ADAPTER_KEYS)[number];

const ADAPTER_LABEL_KEY: Record<AdapterKey, string> = {
  'email-canonical': 'email_canonical',
  'email-disposable': 'email_disposable',
  'phone-linetype': 'phone_linetype',
  'phone-required-earn': 'phone_required_earn',
  'identity-ring': 'identity_ring',
  'email-catchall-domain': 'email_catchall_domain',
  'bot-signup': 'bot_signup',
  'device-fingerprint': 'device_fingerprint',
  'ip-cluster': 'ip_cluster',
  'ip-reputation': 'ip_reputation',
  'payment-instrument': 'payment_instrument',
  'velocity-conversion': 'velocity_conversion',
  'referral-graph': 'referral_graph',
};

const FRAUD_ACTIONS: FraudAction[] = ['pass', 'flag', 'hold', 'block'];
const ACTION_SENTINEL = '_default_';

interface AdapterDraft {
  enabled: boolean;
  actionValue: string;
  params?: Record<string, unknown>;
}

interface PolicyDraft {
  disputeWindowDays: number;
  reservePct: number;
  reserveReleaseDays: number;
}

function buildDraftAdapters(
  fraudConfig: Record<string, AdapterOverride>,
): Record<string, AdapterDraft> {
  const draft: Record<string, AdapterDraft> = {};
  for (const key of ADAPTER_KEYS) {
    const cfg = fraudConfig[key] ?? { enabled: true };
    draft[key] = {
      enabled: cfg.enabled,
      actionValue: cfg.action ?? ACTION_SENTINEL,
      params: cfg.params,
    };
  }
  return draft;
}

function buildFraudConfigPayload(
  drafts: Record<string, AdapterDraft>,
): Record<string, AdapterOverride> {
  const out: Record<string, AdapterOverride> = {};
  for (const key of ADAPTER_KEYS) {
    const d = drafts[key];
    if (!d) continue;
    const override: AdapterOverride = { enabled: d.enabled };
    if (d.actionValue !== ACTION_SENTINEL) {
      override.action = d.actionValue as FraudAction;
    }
    if (d.params !== undefined) {
      override.params = d.params;
    }
    out[key] = override;
  }
  return out;
}

function FraudConfigCardsSkeleton() {
  return (
    <div aria-hidden="true" role="presentation" className="space-y-6">
      <div>
        <Skeleton className="mb-3 h-6 w-48" />
        <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
          {ADAPTER_KEYS.map((key) => (
            <article
              key={key}
              className="border-border-default bg-surface-base rounded-lg border p-4"
            >
              <div className="mb-3 flex items-center justify-between gap-3">
                <Skeleton className="h-4 w-32" />
                <Skeleton className="h-6 w-10 rounded-full" />
              </div>
              <Skeleton className="mb-1 h-3 w-28" />
              <Skeleton className="h-10 w-full" />
            </article>
          ))}
        </div>
      </div>

      <article className="border-border-default bg-surface-base rounded-lg border p-5">
        <Skeleton className="mb-4 h-6 w-44" />
        <div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
          {Array.from({ length: 3 }).map((_, index) => (
            <div key={index}>
              <Skeleton className="mb-1 h-4 w-28" />
              <Skeleton className="h-10 w-full" />
              <Skeleton className="mt-1 h-3 w-36" />
            </div>
          ))}
        </div>
      </article>

      <div className="flex justify-end">
        <Skeleton className="h-10 w-28" />
      </div>
    </div>
  );
}

export function FraudConfigCards() {
  const t = useT('admin_affiliates');
  const tCommon = useT('common');
  const qc = useQueryClient();

  const settingsQuery = useQuery<SettingsData>({
    queryKey: ['affiliate-settings'],
    queryFn: async () => {
      const res = await fetchWithRefresh('/api/admin/affiliates/settings');
      if (!res.ok) throw new Error(await res.text());
      const json = (await res.json()) as { data: SettingsData };
      return json.data;
    },
    staleTime: 30_000,
  });

  const [adapters, setAdapters] = useState<Record<string, AdapterDraft> | null>(null);
  const [policy, setPolicy] = useState<PolicyDraft | null>(null);
  const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle');

  const save = useMutation({
    mutationFn: async () => {
      const adapterDrafts =
        adapters != null ? adapters : buildDraftAdapters(settingsQuery.data?.fraudConfig ?? {});
      const nextPolicy =
        policy ??
        (settingsQuery.data
          ? {
              disputeWindowDays: settingsQuery.data.disputeWindowDays,
              reservePct: settingsQuery.data.reservePct,
              reserveReleaseDays: settingsQuery.data.reserveReleaseDays,
            }
          : { disputeWindowDays: 120, reservePct: 0, reserveReleaseDays: 120 });
      const res = await fetchWithRefresh('/api/admin/affiliates/settings', {
        method: 'PATCH',
        headers: {
          'Content-Type': 'application/json',
          'x-csrf-token': getCsrfToken(),
        },
        body: JSON.stringify({
          fraudConfig: buildFraudConfigPayload(adapterDrafts),
          disputeWindowDays: nextPolicy.disputeWindowDays,
          reservePct: nextPolicy.reservePct,
          reserveReleaseDays: nextPolicy.reserveReleaseDays,
        }),
      });
      if (!res.ok) throw new Error(await res.text());
      return res.json();
    },
    onSuccess: () => {
      setSaveStatus('saved');
      void qc.invalidateQueries({ queryKey: ['affiliate-settings'] });
      setTimeout(() => setSaveStatus('idle'), 2500);
    },
    onError: () => {
      setSaveStatus('error');
      setTimeout(() => setSaveStatus('idle'), 3000);
    },
    onMutate: () => {
      setSaveStatus('saving');
    },
  });

  function setAdapterEnabled(key: string, enabled: boolean) {
    setAdapters((prev) => {
      const base = prev ?? buildDraftAdapters(settingsQuery.data?.fraudConfig ?? {});
      return {
        ...base,
        [key]: { ...base[key]!, enabled },
      };
    });
  }

  function setAdapterAction(key: string, actionValue: string) {
    setAdapters((prev) => {
      const base = prev ?? buildDraftAdapters(settingsQuery.data?.fraudConfig ?? {});
      return {
        ...base,
        [key]: { ...base[key]!, actionValue },
      };
    });
  }

  function setPolicyField(field: keyof PolicyDraft, value: number) {
    if (Number.isNaN(value)) return;
    setPolicy((prev) => ({
      ...(prev ??
        (settingsQuery.data
          ? {
              disputeWindowDays: settingsQuery.data.disputeWindowDays,
              reservePct: settingsQuery.data.reservePct,
              reserveReleaseDays: settingsQuery.data.reserveReleaseDays,
            }
          : { disputeWindowDays: 120, reservePct: 0, reserveReleaseDays: 120 })),
      [field]: value,
    }));
  }

  const saveBtnLabel =
    saveStatus === 'saving'
      ? t('fraud_config_saving')
      : saveStatus === 'saved'
        ? t('fraud_config_saved')
        : saveStatus === 'error'
          ? t('fraud_config_error')
          : t('fraud_config_save');

  return (
    <QueryBoundary
      query={settingsQuery}
      skeleton={<FraudConfigCardsSkeleton />}
      errorFallback={() => (
        <ErrorState
          title={tCommon('error_loading')}
          action={
            <Button variant="secondary" size="sm" onClick={() => settingsQuery.refetch()}>
              {tCommon('retry')}
            </Button>
          }
        />
      )}
    >
      {(resolvedData) => {
        const resolvedAdapters = adapters ?? buildDraftAdapters(resolvedData.fraudConfig ?? {});
        const resolvedPolicy = policy ?? {
          disputeWindowDays: resolvedData.disputeWindowDays,
          reservePct: resolvedData.reservePct,
          reserveReleaseDays: resolvedData.reserveReleaseDays,
        };

        return (
          <div className="space-y-6">
            <div>
              <h2 className="mb-3 text-base font-semibold text-neutral-900">
                {t('fraud_config_adapters_title')}
              </h2>
              <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
                {ADAPTER_KEYS.map((key) => {
                  const draft = resolvedAdapters[key];
                  if (!draft) return null;
                  const labelKey = `fraud_config_adapter_${ADAPTER_LABEL_KEY[key]}` as const;
                  const adapterLabel = t(labelKey as Parameters<typeof t>[0]);
                  return (
                    <article
                      key={key}
                      className="border-border-default bg-surface-base rounded-lg border p-4"
                      aria-label={adapterLabel}
                    >
                      <div className="mb-3 flex items-center justify-between gap-3">
                        <span className="text-sm font-medium text-neutral-900">{adapterLabel}</span>
                        <Switch
                          id={`adapter-enabled-${key}`}
                          checked={draft.enabled}
                          onCheckedChange={(checked) => setAdapterEnabled(key, checked)}
                          aria-label={`${adapterLabel} — ${t('fraud_config_adapter_enabled')}`}
                        />
                      </div>
                      <div>
                        <TooltipProvider>
                          <Tooltip>
                            <TooltipTrigger asChild>
                              <label
                                htmlFor={`adapter-action-${key}`}
                                className="mb-1 block cursor-help text-xs text-neutral-500"
                              >
                                {t('fraud_config_adapter_action_label')}
                              </label>
                            </TooltipTrigger>
                            <TooltipContent>
                              {t('fraud_config_adapter_action_tooltip')}
                            </TooltipContent>
                          </Tooltip>
                        </TooltipProvider>
                        <Select
                          value={draft.actionValue}
                          onValueChange={(v) => setAdapterAction(key, v)}
                          disabled={!draft.enabled}
                        >
                          <SelectTrigger id={`adapter-action-${key}`}>
                            <SelectValue />
                          </SelectTrigger>
                          <SelectContent>
                            <SelectItem value={ACTION_SENTINEL}>
                              {t('fraud_config_adapter_action_default')}
                            </SelectItem>
                            {FRAUD_ACTIONS.map((action) => (
                              <SelectItem key={action} value={action}>
                                {t(
                                  `fraud_config_adapter_action_${action}` as Parameters<
                                    typeof t
                                  >[0],
                                )}
                              </SelectItem>
                            ))}
                          </SelectContent>
                        </Select>
                      </div>
                    </article>
                  );
                })}
              </div>
            </div>

            <article
              className="border-border-default bg-surface-base rounded-lg border p-5"
              aria-label={t('fraud_config_policy_title')}
            >
              <h2 className="mb-4 text-base font-semibold text-neutral-900">
                {t('fraud_config_policy_title')}
              </h2>
              <div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
                <div>
                  <label
                    htmlFor="policy-dispute-window-days"
                    className="mb-1 block text-sm font-medium text-neutral-700"
                  >
                    {t('fraud_config_dispute_window_days')}
                  </label>
                  <NumberInput
                    id="policy-dispute-window-days"
                    min={30}
                    max={730}
                    value={resolvedPolicy.disputeWindowDays}
                    onChange={(n) => setPolicyField('disputeWindowDays', n)}
                    aria-describedby="policy-dispute-window-days-hint"
                  />
                  <p id="policy-dispute-window-days-hint" className="mt-1 text-xs text-neutral-500">
                    {t('fraud_config_dispute_window_days_hint')}
                  </p>
                </div>
                <div>
                  <label
                    htmlFor="policy-reserve-pct"
                    className="mb-1 block text-sm font-medium text-neutral-700"
                  >
                    {t('fraud_config_reserve_pct')}
                  </label>
                  <NumberInput
                    id="policy-reserve-pct"
                    min={0}
                    max={50}
                    value={resolvedPolicy.reservePct}
                    onChange={(n) => setPolicyField('reservePct', n)}
                    aria-describedby="policy-reserve-pct-hint"
                  />
                  <p id="policy-reserve-pct-hint" className="mt-1 text-xs text-neutral-500">
                    {t('fraud_config_reserve_pct_hint')}
                  </p>
                </div>
                <div>
                  <label
                    htmlFor="policy-reserve-release-days"
                    className="mb-1 block text-sm font-medium text-neutral-700"
                  >
                    {t('fraud_config_reserve_release_days')}
                  </label>
                  <NumberInput
                    id="policy-reserve-release-days"
                    min={0}
                    max={730}
                    value={resolvedPolicy.reserveReleaseDays}
                    onChange={(n) => setPolicyField('reserveReleaseDays', n)}
                    aria-describedby="policy-reserve-release-days-hint"
                  />
                  <p
                    id="policy-reserve-release-days-hint"
                    className="mt-1 text-xs text-neutral-500"
                  >
                    {t('fraud_config_reserve_release_days_hint')}
                  </p>
                </div>
              </div>
            </article>

            <div className="flex justify-end">
              <Button
                type="button"
                onClick={() => save.mutate()}
                disabled={save.isPending}
                aria-live="polite"
              >
                {saveBtnLabel}
              </Button>
            </div>
          </div>
        );
      }}
    </QueryBoundary>
  );
}
