'use client';

/**
 * Brochures admin panel (Phase 7 §5.8).
 *
 * Lists every brochure for the port (including archived). Lets a
 * `manage_settings` admin:
 *   - Create new brochures.
 *   - Upload a new version (direct-to-storage presigned PUT, see §11.1).
 *   - Mark default / archive.
 */
import { useState } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { Archive, FileText, Loader2, Plus, Star, Upload } from 'lucide-react';
import { toast } from 'sonner';

import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Switch } from '@/components/ui/switch';
import { apiFetch } from '@/lib/api/client';
import { toastError } from '@/lib/api/toast-error';

interface BrochureRow {
  id: string;
  label: string;
  description: string | null;
  isDefault: boolean;
  archivedAt: string | null;
  versionCount: number;
  currentVersion: {
    id: string;
    fileName: string;
    fileSizeBytes: number;
    uploadedAt: string;
  } | null;
}

interface BrochuresResponse {
  data: BrochureRow[];
}

interface UploadGrantResponse {
  data: { storageKey: string; uploadUrl: string; method: 'PUT'; maxBytes: number };
}

export function BrochuresAdminPanel() {
  const queryClient = useQueryClient();
  const [createOpen, setCreateOpen] = useState(false);

  const brochuresQuery = useQuery<BrochuresResponse>({
    queryKey: ['brochures', 'admin'],
    queryFn: () => apiFetch('/api/v1/admin/brochures'),
  });

  const rows = brochuresQuery.data?.data ?? [];

  return (
    <div className="space-y-4">
      <div className="flex justify-end">
        <Button onClick={() => setCreateOpen(true)}>
          <Plus className="mr-2 h-4 w-4" aria-hidden /> New brochure
        </Button>
      </div>

      {brochuresQuery.isLoading && (
        <div className="flex items-center gap-2 text-sm text-muted-foreground">
          <Loader2 className="h-4 w-4 animate-spin" aria-hidden /> Loading…
        </div>
      )}

      {!brochuresQuery.isLoading && rows.length === 0 && (
        <Card>
          <CardContent className="py-8 text-center text-sm text-muted-foreground">
            No brochures yet. Click &ldquo;New brochure&rdquo; to add one.
          </CardContent>
        </Card>
      )}

      <div className="space-y-3">
        {rows.map((b) => (
          <BrochureCard
            key={b.id}
            brochure={b}
            onChange={() => {
              void queryClient.invalidateQueries({ queryKey: ['brochures', 'admin'] });
              void queryClient.invalidateQueries({ queryKey: ['brochures', 'list'] });
            }}
          />
        ))}
      </div>

      <CreateBrochureDialog
        open={createOpen}
        onOpenChange={setCreateOpen}
        onCreated={() => {
          void queryClient.invalidateQueries({ queryKey: ['brochures', 'admin'] });
        }}
      />
    </div>
  );
}

function BrochureCard({ brochure, onChange }: { brochure: BrochureRow; onChange: () => void }) {
  const [uploading, setUploading] = useState(false);

  const setDefaultMutation = useMutation({
    mutationFn: () =>
      apiFetch(`/api/v1/admin/brochures/${brochure.id}`, {
        method: 'PATCH',
        body: { isDefault: true },
      }),
    onSuccess: () => {
      toast.success('Default brochure updated');
      onChange();
    },
    onError: (e) => toastError(e),
  });

  const archiveMutation = useMutation({
    mutationFn: () => apiFetch(`/api/v1/admin/brochures/${brochure.id}`, { method: 'DELETE' }),
    onSuccess: () => {
      toast.success('Brochure archived');
      onChange();
    },
    onError: (e) => toastError(e),
  });

  async function handleUpload(file: File) {
    setUploading(true);
    try {
      const grant: UploadGrantResponse = await apiFetch(
        `/api/v1/admin/brochures/${brochure.id}/versions`,
      );
      if (file.size > grant.data.maxBytes) {
        throw new Error(
          `File is too large. Max is ${(grant.data.maxBytes / 1024 / 1024).toFixed(0)}MB.`,
        );
      }
      // Direct-to-storage PUT (§11.1).
      const putRes = await fetch(grant.data.uploadUrl, {
        method: 'PUT',
        body: file,
        headers: { 'Content-Type': 'application/pdf' },
      });
      if (!putRes.ok) throw new Error(`Upload failed: ${putRes.status}`);

      const sha = await sha256Hex(file);
      await apiFetch(`/api/v1/admin/brochures/${brochure.id}/versions`, {
        method: 'POST',
        body: {
          storageKey: grant.data.storageKey,
          fileName: file.name,
          fileSizeBytes: file.size,
          contentSha256: sha,
        },
      });
      toast.success('New version uploaded');
      onChange();
    } catch (err) {
      toastError(err);
    } finally {
      setUploading(false);
    }
  }

  return (
    <Card className={brochure.archivedAt ? 'opacity-60' : ''}>
      <CardHeader>
        <CardTitle className="flex items-center justify-between text-base">
          <span className="flex items-center gap-2">
            <FileText className="h-4 w-4" aria-hidden /> {brochure.label}
            {brochure.isDefault && (
              <span className="flex items-center gap-1 rounded bg-primary/10 px-2 py-0.5 text-xs text-primary">
                <Star className="h-3 w-3" aria-hidden /> default
              </span>
            )}
            {brochure.archivedAt && (
              <span className="rounded bg-muted px-2 py-0.5 text-xs text-muted-foreground">
                archived
              </span>
            )}
          </span>
          <span className="text-xs text-muted-foreground">{brochure.versionCount} versions</span>
        </CardTitle>
      </CardHeader>
      <CardContent className="space-y-2">
        {brochure.description && (
          <p className="text-sm text-muted-foreground">{brochure.description}</p>
        )}
        {brochure.currentVersion && (
          <p className="text-xs text-muted-foreground">
            Latest: {brochure.currentVersion.fileName} (
            {(brochure.currentVersion.fileSizeBytes / 1024 / 1024).toFixed(2)} MB,{' '}
            {new Date(brochure.currentVersion.uploadedAt).toLocaleDateString()})
          </p>
        )}
        <div className="flex gap-2 pt-2">
          {!brochure.archivedAt && (
            <>
              <label className="cursor-pointer">
                <input
                  type="file"
                  accept="application/pdf"
                  className="hidden"
                  onChange={(e) => {
                    const file = e.target.files?.[0];
                    if (file) void handleUpload(file);
                    e.target.value = '';
                  }}
                />
                <Button asChild variant="outline" size="sm" disabled={uploading}>
                  <span>
                    {uploading ? (
                      <Loader2 className="mr-2 h-3 w-3 animate-spin" aria-hidden />
                    ) : (
                      <Upload className="mr-2 h-3 w-3" aria-hidden />
                    )}
                    Upload version
                  </span>
                </Button>
              </label>
              {!brochure.isDefault && (
                <Button
                  variant="outline"
                  size="sm"
                  onClick={() => setDefaultMutation.mutate()}
                  disabled={setDefaultMutation.isPending}
                >
                  <Star className="mr-2 h-3 w-3" aria-hidden /> Mark default
                </Button>
              )}
              <Button
                variant="outline"
                size="sm"
                onClick={() => archiveMutation.mutate()}
                disabled={archiveMutation.isPending}
              >
                <Archive className="mr-2 h-3 w-3" aria-hidden /> Archive
              </Button>
            </>
          )}
        </div>
      </CardContent>
    </Card>
  );
}

function CreateBrochureDialog({
  open,
  onOpenChange,
  onCreated,
}: {
  open: boolean;
  onOpenChange: (o: boolean) => void;
  onCreated: () => void;
}) {
  const [label, setLabel] = useState('');
  const [description, setDescription] = useState('');
  const [isDefault, setIsDefault] = useState(false);

  const createMutation = useMutation({
    mutationFn: () =>
      apiFetch('/api/v1/admin/brochures', {
        method: 'POST',
        body: {
          label,
          description: description || null,
          isDefault,
        },
      }),
    onSuccess: () => {
      toast.success('Brochure created. Upload a version next.');
      setLabel('');
      setDescription('');
      setIsDefault(false);
      onCreated();
      onOpenChange(false);
    },
    onError: (e) => toastError(e),
  });

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>New brochure</DialogTitle>
          <DialogDescription>
            Create the brochure container, then upload a PDF version on the card that appears.
          </DialogDescription>
        </DialogHeader>
        <div className="space-y-3">
          <div className="space-y-1">
            <Label htmlFor="b-label">Label</Label>
            <Input
              id="b-label"
              value={label}
              onChange={(e) => setLabel(e.target.value)}
              placeholder="General overview"
            />
          </div>
          <div className="space-y-1">
            <Label htmlFor="b-desc">Description (optional)</Label>
            <Textarea
              id="b-desc"
              rows={2}
              value={description}
              onChange={(e) => setDescription(e.target.value)}
            />
          </div>
          <div className="flex items-center justify-between">
            <Label htmlFor="b-def">Set as default</Label>
            <Switch id="b-def" checked={isDefault} onCheckedChange={setIsDefault} />
          </div>
        </div>
        <DialogFooter>
          <Button variant="outline" onClick={() => onOpenChange(false)}>
            Cancel
          </Button>
          <Button
            disabled={!label.trim() || createMutation.isPending}
            onClick={() => createMutation.mutate()}
          >
            {createMutation.isPending && (
              <Loader2 className="mr-2 h-4 w-4 animate-spin" aria-hidden />
            )}
            Create
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

async function sha256Hex(file: File): Promise<string> {
  const buf = await file.arrayBuffer();
  const hash = await crypto.subtle.digest('SHA-256', buf);
  return Array.from(new Uint8Array(hash))
    .map((b) => b.toString(16).padStart(2, '0'))
    .join('');
}
