'use client'

import * as Dialog from '@radix-ui/react-dialog'
import { adminPortalContainer } from '../lib/portal-container.js'
import {
  Copy,
  KeyRound,
  Loader2,
  Plus,
  Shield,
  Trash2,
  AlertTriangle,
  Eye,
  EyeOff,
} from 'lucide-react'
import { type FormEvent, useState } from 'react'
import { toast } from 'sonner'
import { useApiData } from '../lib/useApiData.js'
import { cmsApi } from '../lib/api.js'
import { SettingsCard } from './settings/components.js'

const DIALOG_INPUT_CLASS =
  'w-full rounded-md border border-border bg-input-background px-3 py-2 text-base text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none'

export interface ApiKeysProps {
  onNavigate?: (path: string) => void
  /** When rendered inside the Settings tab shell, drop the page padding and
   * demote the heading so it reads as a section rather than a second page. */
  embedded?: boolean
}

interface ApiKeyRecord {
  id: string
  name: string
  keyPrefix: string
  scopes: ApiKeyScopes
  ipRestrictions: string[] | null
  expiresAt: string | null
  lastUsedAt: string | null
  revokedAt: string | null
  createdAt: string
  user?: { id: string; name: string | null; email: string }
}

interface ApiKeyScopes {
  admin?: boolean
  collections?: string[]
  actions?: ('read' | 'create' | 'update' | 'delete')[]
  globals?: string[]
  media?: boolean
  pageBuilder?: boolean
}

function formatScopes(scopes: ApiKeyScopes): string {
  if (scopes.admin) return 'Full admin'
  const parts: string[] = []
  if (scopes.collections?.length) {
    const cols = scopes.collections.includes('*')
      ? 'all collections'
      : scopes.collections.join(', ')
    const acts = scopes.actions?.join('/') ?? 'read'
    parts.push(`${acts} on ${cols}`)
  }
  if (scopes.globals?.length) parts.push(`globals: ${scopes.globals.join(', ')}`)
  if (scopes.media) parts.push('media')
  if (scopes.pageBuilder) parts.push('pageBuilder')
  return parts.length > 0 ? parts.join(' · ') : 'No scopes'
}

function formatDate(value: string | null): string {
  if (!value) return '—'
  try {
    return new Date(value).toLocaleString()
  } catch {
    return value
  }
}

export function ApiKeys({ embedded = false }: ApiKeysProps = {}) {
  const { data, loading, error, refetch } = useApiData<ApiKeyRecord[]>('/api-keys')
  const [showCreate, setShowCreate] = useState(false)
  const [createdKey, setCreatedKey] = useState<{ key: string; record: ApiKeyRecord } | null>(null)

  const keys = data ?? []
  const rootPadding = embedded ? '' : 'p-4 sm:p-6'

  const handleRevoke = async (id: string, name: string) => {
    if (!confirm(`Revoke API key "${name}"? This cannot be undone.`)) return
    const password = prompt('Confirm your password to revoke this API key:')
    if (!password) return
    const res = await cmsApi(`/api-keys/${id}`, {
      method: 'DELETE',
      headers: { 'x-reauth-password': password },
    })
    if (res.error) {
      toast.error(res.error)
    } else {
      toast.success('API key revoked')
      refetch()
    }
  }

  if (loading) {
    return (
      <div className={`flex h-64 items-center justify-center ${embedded ? '' : 'p-4 sm:p-6'}`}>
        <Loader2 className="text-brand h-6 w-6 animate-spin" />
      </div>
    )
  }

  return (
    <div className={rootPadding}>
      <SettingsCard
        headingLevel="h2"
        title="API Keys"
        description={
          <>
            Long-lived credentials for programmatic access. Use the{' '}
            <code className="bg-muted rounded px-1 py-0.5 text-xs">Authorization: Bearer</code>{' '}
            header instead of session cookies. API key requests skip CSRF.
          </>
        }
        actions={
          <button
            type="button"
            onClick={() => setShowCreate(true)}
            className="bg-brand text-brand-foreground inline-flex items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-opacity hover:opacity-90"
          >
            <Plus size={16} aria-hidden="true" />
            New API Key
          </button>
        }
      >
        {error && (
          <div
            role="alert"
            className="border-destructive/30 bg-destructive/10 text-destructive mb-4 flex items-center gap-3 rounded-lg border p-3 text-sm"
          >
            <AlertTriangle size={20} className="shrink-0" aria-hidden="true" />
            <span className="flex-1">{error}</span>
            <button
              onClick={refetch}
              className="border-destructive/40 rounded-md border px-3 py-1 transition-colors hover:opacity-80"
            >
              Retry
            </button>
          </div>
        )}

        {keys.length === 0 ? (
          <div className="py-10 text-center">
            <KeyRound className="text-muted-foreground mx-auto mb-3 h-8 w-8" aria-hidden="true" />
            <p className="text-foreground mb-1 text-sm font-medium">No API keys yet</p>
            <p className="text-muted-foreground mb-4 text-sm">
              Create one to let AI agents, CI jobs, or external integrations call the CMS API.
            </p>
            <button
              type="button"
              onClick={() => setShowCreate(true)}
              className="bg-brand text-brand-foreground inline-flex items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-opacity hover:opacity-90"
            >
              <Plus size={16} aria-hidden="true" />
              Create your first API key
            </button>
          </div>
        ) : (
          <div className="border-border overflow-hidden rounded-lg border">
            <div className="overflow-x-auto">
              <table className="w-full">
                <thead className="border-border bg-muted/50 border-b">
                  <tr>
                    <th className="text-muted-foreground px-4 py-2 text-left text-xs font-medium">
                      Name
                    </th>
                    <th className="text-muted-foreground px-4 py-2 text-left text-xs font-medium">
                      Token
                    </th>
                    <th className="text-muted-foreground px-4 py-2 text-left text-xs font-medium">
                      Scopes
                    </th>
                    <th className="text-muted-foreground px-4 py-2 text-left text-xs font-medium">
                      Last used
                    </th>
                    <th className="text-muted-foreground px-4 py-2 text-left text-xs font-medium">
                      Expires
                    </th>
                    <th className="text-muted-foreground px-4 py-2 text-left text-xs font-medium">
                      Status
                    </th>
                    <th className="px-4 py-2"></th>
                  </tr>
                </thead>
                <tbody className="divide-border divide-y">
                  {keys.map((k) => {
                    const revoked = !!k.revokedAt
                    const expired = k.expiresAt && new Date(k.expiresAt).getTime() < Date.now()
                    return (
                      <tr key={k.id} className={revoked || expired ? 'opacity-60' : undefined}>
                        <td className="px-4 py-3 text-sm">
                          <div className="text-foreground font-medium">{k.name}</div>
                          {k.user && (
                            <div className="text-muted-foreground text-xs">
                              created by {k.user.name ?? k.user.email}
                            </div>
                          )}
                        </td>
                        <td className="text-muted-foreground px-4 py-3 font-mono text-sm">
                          {k.keyPrefix}…
                        </td>
                        <td className="text-foreground max-w-xs px-4 py-3 text-sm">
                          {formatScopes(k.scopes)}
                        </td>
                        <td className="text-muted-foreground px-4 py-3 text-sm">
                          {formatDate(k.lastUsedAt)}
                        </td>
                        <td className="text-muted-foreground px-4 py-3 text-sm">
                          {formatDate(k.expiresAt)}
                        </td>
                        <td className="px-4 py-3">
                          {revoked ? (
                            <span className="bg-destructive/10 text-destructive inline-flex rounded-full px-2 py-0.5 text-xs font-medium">
                              Revoked
                            </span>
                          ) : expired ? (
                            <span className="bg-warning/10 text-warning inline-flex rounded-full px-2 py-0.5 text-xs font-medium">
                              Expired
                            </span>
                          ) : (
                            <span className="bg-success/10 text-success inline-flex rounded-full px-2 py-0.5 text-xs font-medium">
                              Active
                            </span>
                          )}
                        </td>
                        <td className="px-4 py-3 text-right">
                          {!revoked && (
                            <button
                              type="button"
                              onClick={() => handleRevoke(k.id, k.name)}
                              className="hover:bg-destructive/10 rounded p-1.5 transition-colors"
                              aria-label={`Revoke ${k.name}`}
                              title="Revoke"
                            >
                              <Trash2 className="text-destructive h-4 w-4" aria-hidden="true" />
                            </button>
                          )}
                        </td>
                      </tr>
                    )
                  })}
                </tbody>
              </table>
            </div>
          </div>
        )}
      </SettingsCard>

      <CreateApiKeyDialog
        open={showCreate}
        onClose={() => setShowCreate(false)}
        onCreated={(created) => {
          setShowCreate(false)
          setCreatedKey(created)
          refetch()
        }}
      />

      {createdKey && <RevealKeyDialog created={createdKey} onClose={() => setCreatedKey(null)} />}
    </div>
  )
}

interface CreateApiKeyDialogProps {
  open: boolean
  onClose: () => void
  onCreated: (created: { key: string; record: ApiKeyRecord }) => void
}

function CreateApiKeyDialog({ open, onClose, onCreated }: CreateApiKeyDialogProps) {
  const [name, setName] = useState('')
  const [preset, setPreset] = useState<'admin' | 'content' | 'readonly' | 'custom'>('content')
  const [pageBuilder, setPageBuilder] = useState(true)
  const [media, setMedia] = useState(true)
  const [expiresInDays, setExpiresInDays] = useState<string>('')
  const [password, setPassword] = useState('')
  const [submitting, setSubmitting] = useState(false)

  function buildScopes(): ApiKeyScopes {
    if (preset === 'admin') return { admin: true }
    if (preset === 'readonly') {
      return {
        collections: ['*'],
        actions: ['read'],
        globals: ['*'],
        media: false,
        pageBuilder: false,
      }
    }
    if (preset === 'content') {
      return {
        collections: ['*'],
        actions: ['read', 'create', 'update'],
        globals: ['*'],
        media,
        pageBuilder,
      }
    }
    return {
      collections: ['*'],
      actions: ['read'],
      media,
      pageBuilder,
    }
  }

  async function handleSubmit(e: FormEvent) {
    e.preventDefault()
    if (!name.trim()) return toast.error('Name is required')
    if (!password) return toast.error('Password confirmation is required')

    setSubmitting(true)
    try {
      const scopes = buildScopes()
      const body: Record<string, unknown> = { name: name.trim(), scopes }
      const days = parseInt(expiresInDays, 10)
      if (!isNaN(days) && days > 0) {
        body.expiresAt = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toISOString()
      }
      const res = await cmsApi<{ key: string } & ApiKeyRecord>('/api-keys', {
        method: 'POST',
        headers: { 'x-reauth-password': password, 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
      })
      if (res.error || !res.data) {
        toast.error(res.error ?? 'Failed to create API key')
        return
      }
      const { key, ...record } = res.data
      onCreated({ key, record: record as ApiKeyRecord })
      setName('')
      setPassword('')
      setExpiresInDays('')
    } finally {
      setSubmitting(false)
    }
  }

  return (
    <Dialog.Root open={open} onOpenChange={(o) => !o && onClose()}>
      <Dialog.Portal container={adminPortalContainer()}>
        <Dialog.Overlay className="fixed inset-0 z-40 bg-black/40" />
        <Dialog.Content className="border-border bg-card fixed top-1/2 left-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border p-6 shadow-xl">
          <Dialog.Title className="text-card-foreground mb-4 flex items-center gap-2 text-lg font-medium">
            <Shield className="text-brand h-5 w-5" aria-hidden="true" />
            New API Key
          </Dialog.Title>
          <form onSubmit={handleSubmit} className="space-y-4">
            <div>
              <label
                htmlFor="apikey-name"
                className="text-foreground mb-1 block text-sm font-medium"
              >
                Name
              </label>
              <input
                id="apikey-name"
                type="text"
                value={name}
                onChange={(e) => setName(e.target.value)}
                placeholder="e.g. AI agent — production"
                className={DIALOG_INPUT_CLASS}
                required
                maxLength={100}
              />
            </div>

            <div>
              <span className="text-foreground mb-2 block text-sm font-medium">Preset</span>
              <div className="grid grid-cols-2 gap-2">
                {(['content', 'readonly', 'admin', 'custom'] as const).map((p) => (
                  <button
                    key={p}
                    type="button"
                    onClick={() => setPreset(p)}
                    aria-pressed={preset === p}
                    className={`rounded-md border px-3 py-2 text-left text-sm transition-colors ${
                      preset === p
                        ? 'border-brand bg-brand/5 text-foreground'
                        : 'border-border text-foreground hover:bg-accent'
                    }`}
                  >
                    <div className="font-medium capitalize">{p}</div>
                    <div className="text-muted-foreground text-xs">
                      {p === 'content' && 'Read/create/update content'}
                      {p === 'readonly' && 'Read-only access'}
                      {p === 'admin' && 'Full admin access'}
                      {p === 'custom' && 'Pick capabilities'}
                    </div>
                  </button>
                ))}
              </div>
            </div>

            {(preset === 'content' || preset === 'custom') && (
              <div className="border-border bg-muted/50 space-y-2 rounded-md border p-3">
                <label className="text-foreground flex items-center gap-2 text-sm">
                  <input
                    type="checkbox"
                    checked={media}
                    onChange={(e) => setMedia(e.target.checked)}
                    className="accent-brand h-4 w-4"
                  />
                  Allow media uploads
                </label>
                <label className="text-foreground flex items-center gap-2 text-sm">
                  <input
                    type="checkbox"
                    checked={pageBuilder}
                    onChange={(e) => setPageBuilder(e.target.checked)}
                    className="accent-brand h-4 w-4"
                  />
                  Allow AI page generation
                </label>
              </div>
            )}

            <div>
              <label
                htmlFor="apikey-expiry"
                className="text-foreground mb-1 block text-sm font-medium"
              >
                Expires in (days)
              </label>
              <input
                id="apikey-expiry"
                type="number"
                min={1}
                placeholder="Never (leave blank)"
                value={expiresInDays}
                onChange={(e) => setExpiresInDays(e.target.value)}
                className={DIALOG_INPUT_CLASS}
              />
            </div>

            <div>
              <label
                htmlFor="apikey-password"
                className="text-foreground mb-1 block text-sm font-medium"
              >
                Confirm password
              </label>
              <input
                id="apikey-password"
                type="password"
                value={password}
                onChange={(e) => setPassword(e.target.value)}
                className={DIALOG_INPUT_CLASS}
                required
              />
              <p className="text-muted-foreground mt-1 text-sm">
                Creating an API key is a sensitive action and requires re-authentication.
              </p>
            </div>

            <div className="flex items-center justify-end gap-2 pt-2">
              <button
                type="button"
                onClick={onClose}
                className="border-border text-foreground hover:bg-accent rounded-md border px-4 py-2 text-sm transition-colors"
              >
                Cancel
              </button>
              <button
                type="submit"
                disabled={submitting}
                className="bg-brand text-brand-foreground rounded-md px-4 py-2 text-sm font-medium transition-opacity hover:opacity-90 disabled:opacity-50"
              >
                {submitting ? 'Creating…' : 'Create key'}
              </button>
            </div>
          </form>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  )
}

function RevealKeyDialog({
  created,
  onClose,
}: {
  created: { key: string; record: ApiKeyRecord }
  onClose: () => void
}) {
  const [shown, setShown] = useState(false)
  return (
    <Dialog.Root open onOpenChange={(o) => !o && onClose()}>
      <Dialog.Portal container={adminPortalContainer()}>
        <Dialog.Overlay className="fixed inset-0 z-40 bg-black/40" />
        <Dialog.Content className="border-border bg-card fixed top-1/2 left-1/2 z-50 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border p-6 shadow-xl">
          <Dialog.Title className="text-card-foreground mb-2 flex items-center gap-2 text-lg font-medium">
            <Shield className="text-success h-5 w-5" aria-hidden="true" />
            API key created
          </Dialog.Title>
          <Dialog.Description className="text-muted-foreground mb-4 text-sm">
            Copy this key now — it will not be shown again. Treat it like a password.
          </Dialog.Description>

          <div className="border-warning/30 bg-warning/10 mb-4 flex items-start gap-2 rounded-lg border p-3">
            <AlertTriangle className="text-warning mt-0.5 h-4 w-4 shrink-0" aria-hidden="true" />
            <p className="text-foreground text-xs">
              We only store a hash of this key. If you lose it, you&apos;ll need to revoke and
              create a new one.
            </p>
          </div>

          <div className="mb-4 flex items-center gap-2">
            <code className="bg-muted text-foreground flex-1 rounded px-3 py-2 font-mono text-sm break-all">
              {shown ? created.key : '•'.repeat(Math.min(48, created.key.length))}
            </code>
            <button
              type="button"
              onClick={() => setShown((s) => !s)}
              className="border-border text-foreground hover:bg-accent rounded-md border p-2 transition-colors"
              aria-label={shown ? 'Hide key' : 'Show key'}
            >
              {shown ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
            </button>
            <button
              type="button"
              onClick={() => {
                navigator.clipboard.writeText(created.key)
                toast.success('Copied to clipboard')
              }}
              className="border-border text-foreground hover:bg-accent rounded-md border p-2 transition-colors"
              aria-label="Copy key"
            >
              <Copy className="h-4 w-4" />
            </button>
          </div>

          <div className="flex justify-end">
            <button
              type="button"
              onClick={onClose}
              className="bg-brand text-brand-foreground rounded-md px-4 py-2 text-sm font-medium transition-opacity hover:opacity-90"
            >
              I&apos;ve saved it
            </button>
          </div>
        </Dialog.Content>
      </Dialog.Portal>
    </Dialog.Root>
  )
}
