import { Copy, Download, Key, Plus, Trash2 } from 'lucide-react';
import { toast } from 'sonner';

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

import { Head } from '@inertiajs/react';

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 {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/Components/ui/dialog';
import { EmptyState } from '@/Components/ui/empty-state';
import { ErrorWithRetry } from '@/Components/ui/error-with-retry';
import { Input } from '@/Components/ui/input';
import { Label } from '@/Components/ui/label';
import { LoadingButton } from '@/Components/ui/loading-button';
import { Skeleton } from '@/Components/ui/skeleton';
import DashboardLayout from '@/Layouts/DashboardLayout';
import { trackProductEvent } from '@/lib/analytics';
import { copyToClipboard } from '@/lib/clipboard';
import { EMPTY_TITLE } from '@/lib/emptyStateMessages';
import { FEATURE_USED, SETTINGS_API_TOKENS_VIEWED } from '@/lib/event-catalog';
import { formatDateOnly } from '@/lib/format';
import { copied, couldnt } from '@/lib/messages';
import { getCsrfToken } from '@/lib/utils';

interface ApiToken {
  id: number;
  name: string;
  abilities: string[];
  last_used_at: string | null;
  expires_at: string | null;
  created_at: string;
}

export default function ApiTokens() {
  const [tokens, setTokens] = useState<ApiToken[]>([]);
  const [loading, setLoading] = useState(true);
  const [fetchError, setFetchError] = useState(false);
  const [showCreate, setShowCreate] = useState(false);
  const [deleteTarget, setDeleteTarget] = useState<ApiToken | null>(null);

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

  const fetchTokens = useCallback(async () => {
    setFetchError(false);
    try {
      const res = await fetch('/api/tokens', {
        headers: { Accept: 'application/json' },
      });
      if (!res.ok) throw new Error('fetch failed');
      const data = await res.json();
      setTokens(data);
    } catch {
      setFetchError(true);
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    fetchTokens();
  }, [fetchTokens]);

  const handleDelete = async () => {
    if (!deleteTarget) return;
    const res = await fetch(`/api/tokens/${deleteTarget.id}`, {
      method: 'DELETE',
      headers: { Accept: 'application/json', 'X-XSRF-TOKEN': getCsrfToken() },
    });
    setDeleteTarget(null);
    if (!res.ok) {
      toast.error(couldnt('revoke', 'token'));
      return;
    }
    fetchTokens();
    toast.success('API token revoked.');
  };

  return (
    <DashboardLayout>
      <Head title="API Tokens" />
      <EditorialPageHeader
        title="API Tokens"
        subtitle="Personal access tokens. One per integration; rotate them when in doubt."
        actions={
          <Button onClick={() => setShowCreate(true)}>
            <Plus className="mr-2 size-4" />
            Create token
          </Button>
        }
      />
      <div className="container py-8">
        <div className="max-w-4xl mx-auto space-y-6">
          {loading ? (
            <Card>
              <CardContent className="py-6">
                <div className="space-y-3">
                  {[...Array(3)].map((_, i) => (
                    <div key={i} className="flex items-center gap-3 p-4 border rounded-lg">
                      <Skeleton className="h-5 w-1/3" />
                      <Skeleton className="h-4 w-1/4 ml-auto" />
                      <Skeleton className="h-8 w-16" />
                    </div>
                  ))}
                </div>
              </CardContent>
            </Card>
          ) : fetchError ? (
            <Card>
              <CardContent>
                <ErrorWithRetry
                  title="Couldn't load your tokens"
                  message="We hit an error fetching your API tokens."
                  onRetry={fetchTokens}
                  retrying={loading}
                />
              </CardContent>
            </Card>
          ) : tokens.length === 0 ? (
            <Card>
              <CardContent>
                <EmptyState
                  variant="zero"
                  icon={Key}
                  title={EMPTY_TITLE.apiTokens}
                  description="Create a token to authenticate with the RankWizAI API."
                  primaryAction={{ label: 'Create token', onClick: () => setShowCreate(true) }}
                />
              </CardContent>
            </Card>
          ) : (
            <Card>
              <CardHeader>
                <CardTitle>Your Tokens</CardTitle>
                <CardDescription>
                  Tokens are used to authenticate API requests. Keep them secret.
                </CardDescription>
              </CardHeader>
              <CardContent>
                <div className="space-y-3">
                  {tokens.map((token) => (
                    <div
                      key={token.id}
                      className="flex items-center justify-between rounded-md border px-4 py-3"
                    >
                      <div className="space-y-1 min-w-0 flex-1">
                        <div className="flex items-center gap-2">
                          <span className="font-medium text-sm">{token.name}</span>
                          <div className="flex gap-1">
                            {token.abilities.map((ability) => (
                              <Badge key={ability} variant="outline" className="text-xs">
                                {ability}
                              </Badge>
                            ))}
                          </div>
                        </div>
                        <div className="flex gap-3 text-xs text-muted-foreground">
                          <span>Created {formatDateOnly(token.created_at)}</span>
                          {token.last_used_at && (
                            <span>Last used {formatDateOnly(token.last_used_at)}</span>
                          )}
                          {token.expires_at && (
                            <span>Expires {formatDateOnly(token.expires_at)}</span>
                          )}
                        </div>
                      </div>
                      <Button
                        variant="ghost"
                        size="icon"
                        className="text-destructive hover:text-destructive shrink-0"
                        onClick={() => setDeleteTarget(token)}
                        aria-label={`Revoke token ${token.name}`}
                      >
                        <Trash2 className="size-4" />
                      </Button>
                    </div>
                  ))}
                </div>
              </CardContent>
            </Card>
          )}
        </div>
      </div>

      {showCreate && (
        <CreateTokenDialog
          onClose={() => setShowCreate(false)}
          onCreated={() => {
            setShowCreate(false);
            fetchTokens();
          }}
        />
      )}

      <ConfirmDialog
        open={!!deleteTarget}
        onOpenChange={(open) => !open && setDeleteTarget(null)}
        title="Revoke API Token"
        description="This will permanently revoke this token. Any applications using it will lose access immediately."
        resourceName={deleteTarget?.name}
        resourceType="Token"
        confirmLabel="Revoke"
        variant="destructive"
        onConfirm={handleDelete}
      />
    </DashboardLayout>
  );
}

const AVAILABLE_ABILITIES = [
  { value: 'read', label: 'Read', description: 'Read-only access' },
  { value: 'write', label: 'Write', description: 'Create and update resources' },
  { value: 'delete', label: 'Delete', description: 'Delete resources' },
] as const;

/**
 * R3FE-009: Build a datetime-local min string from local wall-clock time.
 * toISOString() is UTC — datetime-local inputs use local time, so UTC min
 * sits hours in the past (ahead-of-UTC) or future (behind-UTC).
 */
function localDatetimeLocal(date: Date): string {
  const pad = (n: number) => String(n).padStart(2, '0');
  return (
    `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` +
    `T${pad(date.getHours())}:${pad(date.getMinutes())}`
  );
}

/**
 * R3UXA-018: Return a Date ~N days from now (local midnight offset).
 */
function daysFromNow(days: number): Date {
  const d = new Date();
  d.setDate(d.getDate() + days);
  return d;
}

function CreateTokenDialog({ onClose, onCreated }: { onClose: () => void; onCreated: () => void }) {
  const [name, setName] = useState('');
  const [abilities, setAbilities] = useState<string[]>(['read']);
  const [expiresAt, setExpiresAt] = useState('');
  const [creating, setCreating] = useState(false);
  const [errors, setErrors] = useState<Record<string, string[]>>({});
  const [createdToken, setCreatedToken] = useState<string | null>(null);
  // R3UXA-002: acknowledgment gate — Done is disabled until checked.
  const [acknowledged, setAcknowledged] = useState(false);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();

    // F-WEBHOOKS_INTEGRATIONS-001 (P0): the backend now requires at least one ability
    // (no wildcard default). Client-side guard so the user sees the error inline
    // before the round-trip.
    if (abilities.length === 0) {
      setErrors({ abilities: ['Please select at least one permission for this token.'] });
      return;
    }

    setCreating(true);
    setErrors({});

    const res = await fetch('/api/tokens', {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
        'X-XSRF-TOKEN': getCsrfToken(),
      },
      body: JSON.stringify({
        name,
        abilities,
        expires_at: expiresAt || undefined,
      }),
    });

    const data = await res.json();
    setCreating(false);

    if (!res.ok) {
      if (res.status === 422 && data.errors) {
        setErrors(data.errors);
      } else {
        toast.error(data.message || couldnt('create', 'token'));
      }
      return;
    }

    setCreatedToken(data.token);
    toast.success('API token created.');
  };

  // R3UXA-001: hardened copy using the lib/clipboard wrapper.
  const handleCopyToken = async () => {
    if (!createdToken) return;
    const ok = await copyToClipboard(createdToken);
    if (ok) {
      toast.success(copied('Token'));
    } else {
      toast.error(couldnt('copy', 'token'));
    }
  };

  // R3UXA-018: download token as a .txt file so the user has a second save path.
  const handleDownloadToken = () => {
    if (!createdToken) return;
    const blob = new Blob([createdToken], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `${name || 'api-token'}.txt`;
    a.click();
    URL.revokeObjectURL(url);
  };

  // Wave 5 (UI audit): both panels migrated to the canonical `<Dialog>`
  // primitive — this gives focus trap, Esc-to-close, click-outside, and
  // proper `role="dialog"` + ARIA labelling for free via Radix. The previous
  // implementation was a `<div className="fixed inset-0 ... bg-background/80">`
  // wrapper that had none of those affordances.
  return (
    <Dialog open onOpenChange={(open) => !open && !createdToken && onClose()}>
      {createdToken ? (
        // R3UXA-002: non-dismissible panel — outside click / Esc do not close it.
        // Done button is disabled until the acknowledgment checkbox is ticked.
        <DialogContent
          size="md"
          onPointerDownOutside={(e) => e.preventDefault()}
          onEscapeKeyDown={(e) => e.preventDefault()}
        >
          <DialogHeader>
            <DialogTitle>Token Created</DialogTitle>
            <DialogDescription>Copy this token now. It will only be shown once.</DialogDescription>
          </DialogHeader>
          <div className="space-y-4">
            <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">
                {createdToken}
              </code>
              <Button
                variant="outline"
                size="icon"
                onClick={handleCopyToken}
                aria-label="Copy API token"
              >
                <Copy className="size-4" />
              </Button>
            </div>
            {/* R3UXA-018: Download as secondary save path */}
            <Button variant="ghost" size="sm" className="w-full gap-2" onClick={handleDownloadToken}>
              <Download className="size-4" />
              Download as .txt
            </Button>
            {/* R3UXA-002: acknowledgment checkbox before Done */}
            <div className="flex items-start gap-3 rounded-md border border-warning-border bg-warning-soft px-3 py-2">
              <Checkbox
                id="token-acknowledged"
                checked={acknowledged}
                onCheckedChange={(checked) => setAcknowledged(checked === true)}
                className="mt-0.5 shrink-0"
              />
              <label htmlFor="token-acknowledged" className="text-sm text-warning-strong cursor-pointer leading-snug">
                I&apos;ve copied and stored this token &mdash; I understand it won&apos;t be shown again.
              </label>
            </div>
            <Button
              className="w-full"
              onClick={onCreated}
              disabled={!acknowledged}
              aria-disabled={!acknowledged}
            >
              Done
            </Button>
          </div>
        </DialogContent>
      ) : (
        <DialogContent size="md">
          <DialogHeader>
            <DialogTitle>Create API Token</DialogTitle>
            <DialogDescription>
              Tokens authenticate your API requests. Choose permissions carefully.
            </DialogDescription>
          </DialogHeader>
          <form onSubmit={handleSubmit} className="space-y-4">
            <div className="space-y-2">
              <Label htmlFor="token-name">Token name</Label>
              <Input
                id="token-name"
                placeholder="e.g., Production Server"
                value={name}
                onChange={(e) => setName(e.target.value)}
                required
                aria-describedby={errors.name ? 'token-name-error' : undefined}
                aria-invalid={!!errors.name}
              />
              {errors.name && (
                <p id="token-name-error" className="text-xs text-destructive">
                  {errors.name[0]}
                </p>
              )}
            </div>

            <div className="space-y-2">
              <Label>Permissions</Label>
              <div className="space-y-2">
                {AVAILABLE_ABILITIES.map((ability) => (
                  <label key={ability.value} className="flex items-center gap-2 text-sm">
                    <input
                      type="checkbox"
                      checked={abilities.includes(ability.value)}
                      onChange={(e) =>
                        setAbilities(
                          e.target.checked
                            ? [...abilities, ability.value]
                            : abilities.filter((a) => a !== ability.value),
                        )
                      }
                      className="rounded"
                    />
                    <span className="font-medium">{ability.label}</span>
                    <span className="text-muted-foreground">— {ability.description}</span>
                  </label>
                ))}
              </div>
              {errors.abilities && (
                <p className="text-xs text-destructive" role="alert">
                  {errors.abilities[0]}
                </p>
              )}
            </div>

            <div className="space-y-2">
              {/* R3UXA-018: Preset expiry buttons + timezone clarity */}
              <Label htmlFor="token-expires">Expiration (optional)</Label>
              <div className="flex flex-wrap gap-1.5 mb-1.5">
                {([30, 60, 90] as const).map((days) => (
                  <Button
                    key={days}
                    type="button"
                    variant="outline"
                    size="sm"
                    className="h-7 text-xs"
                    onClick={() => setExpiresAt(localDatetimeLocal(daysFromNow(days)))}
                  >
                    {days} days
                  </Button>
                ))}
                <Button
                  type="button"
                  variant="outline"
                  size="sm"
                  className="h-7 text-xs"
                  onClick={() => setExpiresAt('')}
                >
                  No expiry
                </Button>
              </div>
              {/* R3FE-009: use local datetime to avoid UTC offset shifting the min */}
              <Input
                id="token-expires"
                type="datetime-local"
                value={expiresAt}
                onChange={(e) => setExpiresAt(e.target.value)}
                min={localDatetimeLocal(new Date())}
              />
              <p className="text-xs text-muted-foreground">
                Leave blank for a token that never expires. Times are in your local timezone.
              </p>
              {errors.expires_at && (
                <p className="text-xs text-destructive">{errors.expires_at[0]}</p>
              )}
            </div>

            <DialogFooter className="gap-2 sm:gap-2">
              <Button type="button" variant="outline" className="flex-1" onClick={onClose}>
                Cancel
              </Button>
              <LoadingButton
                type="submit"
                className="flex-1"
                loading={creating}
                loadingText="Creating…"
              >
                Create token
              </LoadingButton>
            </DialogFooter>
          </form>
        </DialogContent>
      )}
    </Dialog>
  );
}
