import DOMPurify from 'dompurify';
import { Copy, Download, Eye, EyeOff, RefreshCw, Shield, ShieldCheck, ShieldOff } from 'lucide-react';
import { toast } from 'sonner';

import { useEffect, useState, FormEventHandler } from 'react';

import { Head, useForm, usePage, router } from '@inertiajs/react';

import InputError from '@/Components/InputError';
import EditorialPageHeader from '@/Components/layout/EditorialPageHeader';
import { Badge } from '@/Components/ui/badge';
import { Button } from '@/Components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/Components/ui/card';
import { Checkbox } from '@/Components/ui/checkbox';
import { ConfirmDialog } from '@/Components/ui/confirm-dialog';
import { Input } from '@/Components/ui/input';
import { InputOTP, InputOTPGroup, InputOTPSlot } from '@/Components/ui/input-otp';
import { Label } from '@/Components/ui/label';
import { LoadingButton } from '@/Components/ui/loading-button';
import { useUnsavedChanges } from '@/hooks/useUnsavedChanges';
import DashboardLayout from '@/Layouts/DashboardLayout';
import { trackProductEvent } from '@/lib/analytics';
import { copyToClipboard } from '@/lib/clipboard';
import { FEATURE_USED, SETTINGS_SECURITY_VIEWED } from '@/lib/event-catalog';
import { copied, couldnt } from '@/lib/messages';
import type { PageProps } from '@/types';

const QR_ALLOWED_TAGS = [
  'svg',
  'rect',
  'path',
  'circle',
  'line',
  'polyline',
  'polygon',
  'g',
  'defs',
  'title',
];
const QR_ALLOWED_ATTR = [
  'viewBox',
  'width',
  'height',
  'fill',
  'stroke',
  'stroke-width',
  'd',
  'cx',
  'cy',
  'r',
  'x',
  'y',
  'xmlns',
];

interface SecurityProps {
  enabled: boolean;
  qr_code: string | null;
  secret: string | null;
  recovery_codes: string[] | null;
}

export default function Security({ enabled, qr_code, secret, recovery_codes }: SecurityProps) {
  const { flash } = usePage<PageProps>().props;

  useEffect(() => {
    trackProductEvent(SETTINGS_SECURITY_VIEWED);
    trackProductEvent(FEATURE_USED, { feature: 'settings_security' });
  }, []);

  return (
    <DashboardLayout>
      <Head title="Security" />
      <EditorialPageHeader title="Security" subtitle="Two-factor auth, active sessions, and password recovery. The basics, done well." />
      <div className="container py-8">
        <div className="max-w-3xl mx-auto space-y-6">
          {flash.success && (
            <div role="status" className="rounded-md border border-success/20 bg-success/5 px-4 py-3 text-sm text-success-strong">
              {flash.success}
            </div>
          )}

          {enabled ? (
            // R3UXA-015: if just-confirmed codes exist, force them through a
            // one-time acknowledgment panel before returning to normal EnabledState.
            <EnabledState initialRecoveryCodes={recovery_codes ?? null} />
          ) : qr_code && secret ? (
            <SetupState qrCode={qr_code} secret={secret} />
          ) : (
            <NotEnabledState />
          )}
        </div>
      </div>
    </DashboardLayout>
  );
}

function NotEnabledState() {
  const { post, processing } = useForm({});

  const handleEnable: FormEventHandler = (e) => {
    e.preventDefault();
    post(route('two-factor.enable'));
  };

  return (
    <Card>
      <CardHeader>
        <div className="flex items-center gap-3">
          <div className="rounded-full bg-muted p-2">
            <Shield className="size-5 text-muted-foreground" />
          </div>
          <div>
            <CardTitle>Two-Factor Authentication</CardTitle>
            <CardDescription>
              Add an extra layer of security to your account using a TOTP authenticator app.
            </CardDescription>
          </div>
        </div>
      </CardHeader>
      <CardContent>
        <div className="space-y-4">
          <div className="flex items-center gap-2">
            <Badge variant="secondary">Not enabled</Badge>
          </div>
          <p className="text-sm text-muted-foreground">
            Two-factor authentication adds an additional layer of security to your account by
            requiring a code from your authenticator app when you sign in.
          </p>
          <form onSubmit={handleEnable}>
            <LoadingButton type="submit" loading={processing} loadingText="Enabling...">
              Enable two-factor authentication
            </LoadingButton>
          </form>
        </div>
      </CardContent>
    </Card>
  );
}

function SetupState({ qrCode, secret }: { qrCode: string; secret: string }) {
  const { data, setData, post, processing, errors, isDirty } = useForm({
    code: '',
  });

  useUnsavedChanges(isDirty);

  const handleConfirm: FormEventHandler = (e) => {
    e.preventDefault();
    post(route('two-factor.confirm'));
  };

  return (
    <Card>
      <CardHeader>
        <div className="flex items-center gap-3">
          <div className="rounded-full bg-primary/10 p-2">
            <Shield className="size-5 text-primary" />
          </div>
          <div>
            <CardTitle>Set Up Two-Factor Authentication</CardTitle>
            <CardDescription>
              Scan the QR code below with your authenticator app, then enter the verification code.
            </CardDescription>
          </div>
        </div>
      </CardHeader>
      <CardContent>
        <div className="space-y-6">
          {/* Outer container: neutral in both light and dark mode */}
          <div className="flex justify-center rounded-lg border border-border p-4 dark:border-border/50">
            {/* bg-white intentional on inner element: QR codes require white background for scanner apps */}
            <div className="bg-white p-2 rounded">
              <div
                dangerouslySetInnerHTML={{
                  __html: DOMPurify.sanitize(qrCode, {
                    ALLOWED_TAGS: QR_ALLOWED_TAGS,
                    ALLOWED_ATTR: QR_ALLOWED_ATTR,
                  }),
                }}
              />
            </div>
          </div>

          <div className="space-y-2">
            <p className="text-sm font-medium">Manual entry key</p>
            <div className="flex items-center gap-2">
              {/* F4UXA-008: standardize on font-data (canonical machine-value token) */}
              <code className="flex-1 rounded-md bg-muted px-3 py-2 text-sm font-data break-all">
                {secret}
              </code>
              {/* R3UXA-001: hardened copy */}
              <Button
                type="button"
                variant="outline"
                size="icon"
                onClick={async () => {
                  const ok = await copyToClipboard(secret);
                  if (ok) toast.success(copied('Secret key'));
                  else toast.error(couldnt('copy', 'secret key'));
                }}
                aria-label="Copy secret key"
              >
                <Copy className="size-4" />
              </Button>
            </div>
          </div>

          <form onSubmit={handleConfirm} className="space-y-4">
            <div className="space-y-2">
              <Label htmlFor="confirm-code">Verification code</Label>
              <div className="flex justify-start">
                <InputOTP
                  id="confirm-code"
                  maxLength={6}
                  value={data.code}
                  onChange={(value) => setData('code', value)}
                  autoFocus
                  aria-required="true"
                  aria-invalid={!!errors.code}
                  aria-describedby={errors.code ? 'confirm-code-error' : undefined}
                >
                  <InputOTPGroup>
                    <InputOTPSlot index={0} />
                    <InputOTPSlot index={1} />
                    <InputOTPSlot index={2} />
                    <InputOTPSlot index={3} />
                    <InputOTPSlot index={4} />
                    <InputOTPSlot index={5} />
                  </InputOTPGroup>
                </InputOTP>
              </div>
              <InputError id="confirm-code-error" message={errors.code} className="text-xs" />
            </div>

            <LoadingButton type="submit" loading={processing} loadingText="Verifying...">
              Confirm and enable
            </LoadingButton>
          </form>
        </div>
      </CardContent>
    </Card>
  );
}

function EnabledState({ initialRecoveryCodes }: { initialRecoveryCodes: string[] | null }) {
  // R3UXA-015: if recovery codes were just generated (first load after confirm),
  // force-show them in a one-time acknowledgment panel.
  const [showRecoveryCodes, setShowRecoveryCodes] = useState(!!initialRecoveryCodes);
  const [recoveryCodes, setRecoveryCodes] = useState<string[] | null>(initialRecoveryCodes);
  const [codesAcknowledged, setCodesAcknowledged] = useState(!initialRecoveryCodes);
  const [loadingCodes, setLoadingCodes] = useState(false);
  const [disableDialogOpen, setDisableDialogOpen] = useState(false);

  const disableForm = useForm({ password: '' });

  useUnsavedChanges(disableForm.isDirty);

  const fetchRecoveryCodes = async () => {
    setLoadingCodes(true);
    try {
      const response = await fetch(route('two-factor.recovery-codes'), {
        headers: { Accept: 'application/json' },
      });
      if (!response.ok) throw new Error();
      const data = await response.json();
      setRecoveryCodes(data.recovery_codes);
      setShowRecoveryCodes(true);
    } catch {
      toast.error(couldnt('load', 'recovery codes'));
    } finally {
      setLoadingCodes(false);
    }
  };

  const handleRegenerate = () => {
    router.post(
      route('two-factor.recovery-codes.regenerate'),
      {},
      {
        onSuccess: () => {
          // F4FE-007: After regenerating, force-show the new codes behind the
          // acknowledgment gate so the user cannot navigate away without capturing them.
          // Reset codesAcknowledged so the Done/Hide button stays gated until confirmed.
          setCodesAcknowledged(false);
          // Fetch the newly generated codes and show them in the acknowledgment panel.
          fetchRecoveryCodes();
        },
      },
    );
  };

  const handleDisable = () => {
    disableForm.delete(route('two-factor.disable'), {
      onSuccess: () => setDisableDialogOpen(false),
      onError: () => {},
    });
  };

  return (
    <>
      <Card>
        <CardHeader>
          <div className="flex items-center justify-between">
            <div className="flex items-center gap-3">
              <div className="rounded-full bg-success/10 p-2">
                <ShieldCheck className="size-5 text-success" />
              </div>
              <div>
                <CardTitle>Two-Factor Authentication</CardTitle>
                <CardDescription>
                  Your account is protected with two-factor authentication.
                </CardDescription>
              </div>
            </div>
            <Badge variant="default" className="bg-success text-success-foreground">
              Enabled
            </Badge>
          </div>
        </CardHeader>
        <CardContent>
          <div className="space-y-4">
            <p className="text-sm text-muted-foreground">
              You will be asked for a verification code from your authenticator app each time you
              sign in.
            </p>

            <div className="flex flex-wrap gap-2">
              {/* R3UXA-015: hide/show disabled until one-time acknowledgment completes */}
              <Button
                variant="outline"
                onClick={showRecoveryCodes ? () => { if (codesAcknowledged) setShowRecoveryCodes(false); } : fetchRecoveryCodes}
                disabled={loadingCodes || (showRecoveryCodes && !codesAcknowledged)}
              >
                {showRecoveryCodes ? (
                  <>
                    <EyeOff className="mr-2 size-4" /> Hide recovery codes
                  </>
                ) : (
                  <>
                    <Eye className="mr-2 size-4" /> View recovery codes
                  </>
                )}
              </Button>
              <Button variant="outline" onClick={handleRegenerate}>
                <RefreshCw className="mr-2 size-4" />
                Regenerate codes
              </Button>
              <Button variant="destructive" onClick={() => setDisableDialogOpen(true)}>
                <ShieldOff className="mr-2 size-4" />
                Disable
              </Button>
            </div>
          </div>
        </CardContent>
      </Card>

      {showRecoveryCodes && recoveryCodes && (
        <Card>
          <CardHeader>
            <CardTitle className="text-base">Recovery Codes</CardTitle>
            <CardDescription>
              Store these codes in a safe place. Each code can only be used once.
            </CardDescription>
          </CardHeader>
          <CardContent>
            <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
              {/* F4UXA-008: standardize on font-data (canonical machine-value token) */}
              {recoveryCodes.map((code) => (
                <code
                  key={code}
                  className="rounded-md bg-muted px-3 py-2 text-sm font-data text-center"
                >
                  {code}
                </code>
              ))}
            </div>
            <div className="mt-4 flex flex-wrap gap-2">
              {/* R3UXA-001: hardened copy */}
              <Button
                variant="outline"
                size="sm"
                onClick={async () => {
                  const ok = await copyToClipboard(recoveryCodes.join('\n'));
                  if (ok) toast.success(copied('Recovery codes'));
                  else toast.error(couldnt('copy', 'recovery codes'));
                }}
              >
                <Copy className="mr-2 size-4" />
                Copy all codes
              </Button>
              {/* R3UXA-015: download as secondary save path */}
              <Button
                variant="ghost"
                size="sm"
                onClick={() => {
                  const blob = new Blob([recoveryCodes.join('\n')], { type: 'text/plain' });
                  const url = URL.createObjectURL(blob);
                  const a = document.createElement('a');
                  a.href = url;
                  a.download = 'recovery-codes.txt';
                  a.click();
                  URL.revokeObjectURL(url);
                }}
              >
                <Download className="mr-2 size-4" />
                Download
              </Button>
            </div>

            {/* R3UXA-015: force acknowledgment before dismissing the one-time panel */}
            {!codesAcknowledged && (
              <div className="mt-4 flex items-start gap-3 rounded-md border border-warning-border bg-warning-soft px-3 py-2">
                <Checkbox
                  id="recovery-codes-acknowledged"
                  checked={codesAcknowledged}
                  onCheckedChange={(checked) => {
                    if (checked) setCodesAcknowledged(true);
                  }}
                  className="mt-0.5 shrink-0"
                />
                <label htmlFor="recovery-codes-acknowledged" className="text-sm text-warning-strong cursor-pointer leading-snug">
                  I&apos;ve stored my recovery codes in a safe place &mdash; I understand each code can only be used once.
                </label>
              </div>
            )}
          </CardContent>
        </Card>
      )}

      <ConfirmDialog
        open={disableDialogOpen}
        onOpenChange={setDisableDialogOpen}
        title="Disable Two-Factor Authentication"
        description={
          <div className="space-y-3">
            <p>
              This will remove the extra security from your account. Enter your password to confirm.
            </p>
            <div className="space-y-2">
              <Label htmlFor="disable-password">Password</Label>
              <Input
                id="disable-password"
                type="password"
                value={disableForm.data.password}
                onChange={(e) => disableForm.setData('password', e.target.value)}
                autoComplete="current-password"
                required
                aria-required="true"
                aria-invalid={!!disableForm.errors.password}
                aria-describedby={disableForm.errors.password ? 'disable-password-error' : undefined}
              />
              <InputError id="disable-password-error" message={disableForm.errors.password} className="text-xs" />
            </div>
          </div>
        }
        confirmLabel="Disable 2FA"
        variant="destructive"
        onConfirm={handleDisable}
      />
    </>
  );
}
