'use client';

import { useEffect, useState } from 'react';
import {
  Loader2,
  Download,
  Database,
  RefreshCw,
  AlertTriangle,
  HardDriveDownload,
} from 'lucide-react';
import { toast } from 'sonner';

import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';
import { apiFetch } from '@/lib/api/client';
import { toastError } from '@/lib/api/toast-error';
import { triggerUrlDownload } from '@/lib/utils/download';

interface BackupJob {
  id: string;
  status: 'pending' | 'running' | 'completed' | 'failed';
  trigger: 'manual' | 'cron';
  triggeredBy: string | null;
  sizeBytes: number | null;
  storagePath: string | null;
  errorMessage: string | null;
  startedAt: string;
  completedAt: string | null;
}

const STATUS_TONE: Record<string, string> = {
  pending: 'bg-muted text-muted-foreground',
  running: 'bg-amber-100 text-amber-900',
  completed: 'bg-emerald-100 text-emerald-900',
  failed: 'bg-rose-100 text-rose-900',
};

function formatBytes(n: number | null): string {
  if (n === null) return '-';
  if (n < 1024) return `${n} B`;
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
  if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
  return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
}

export function BackupAdminPanel() {
  const [jobs, setJobs] = useState<BackupJob[]>([]);
  const [loading, setLoading] = useState(true);
  const [running, setRunning] = useState(false);
  const [confirmOpen, setConfirmOpen] = useState(false);

  useEffect(() => {
    void load();
  }, []);

  async function load() {
    setLoading(true);
    try {
      const res = await apiFetch<{ data: BackupJob[] }>('/api/v1/admin/backup');
      setJobs(res.data);
    } finally {
      setLoading(false);
    }
  }

  async function trigger() {
    setConfirmOpen(false);
    setRunning(true);
    try {
      const res = await apiFetch<{
        data: { id: string; status: 'completed' | 'failed'; error?: string };
      }>('/api/v1/admin/backup', { method: 'POST' });
      if (res.data.status === 'completed') {
        toast.success('Backup completed');
      } else {
        toast.error(`Backup failed: ${res.data.error ?? 'unknown error'}`);
      }
      await load();
    } catch (err) {
      toastError(err);
    } finally {
      setRunning(false);
    }
  }

  async function download(id: string) {
    try {
      const res = await apiFetch<{ data: { url: string } }>(`/api/v1/admin/backup/${id}/download`);
      triggerUrlDownload(res.data.url, `backup-${id}.dump`);
    } catch (err) {
      toastError(err);
    }
  }

  function downloadFullBundle() {
    // Streams a tar (db.dump + every blob + manifest.json) straight to disk via
    // the GET endpoint (cookie auth). The server assembles the bundle before
    // the first byte arrives, so the browser download sits "pending" for a
    // moment on large datasets — flag that so it doesn't look stuck.
    const stamp = new Date().toISOString().replace(/[:.]/g, '-');
    toast.info(
      'Preparing full backup — your download starts once the server finishes assembling it.',
    );
    triggerUrlDownload('/api/v1/admin/backup/export', `pn-crm-backup-${stamp}.tar`);
  }

  return (
    <div className="space-y-4">
      <Card>
        <CardHeader className="flex flex-row items-start justify-between gap-3 space-y-0">
          <div>
            <CardTitle className="text-base flex items-center gap-2">
              <Database className="h-4 w-4" aria-hidden />
              Run a backup now
            </CardTitle>
            <CardDescription>
              Triggers <code>pg_dump --format=custom</code> against the live database and uploads
              the result to the active storage backend.
            </CardDescription>
          </div>
          <div className="flex gap-2">
            <Button variant="ghost" size="sm" onClick={load} disabled={loading || running}>
              <RefreshCw className="mr-1.5 h-3.5 w-3.5" aria-hidden />
              Refresh
            </Button>
            <Button onClick={() => setConfirmOpen(true)} disabled={running}>
              {running && <Loader2 className="mr-1.5 h-4 w-4 animate-spin" aria-hidden />}
              {running ? 'Running…' : 'Run backup'}
            </Button>
          </div>
        </CardHeader>
        <CardContent className="text-xs text-muted-foreground">
          Backups land at <code>backups/&lt;id&gt;.dump</code> via{' '}
          <code>getStorageBackend().put()</code>. Restore is intentionally not exposed in the UI -
          download the .dump file and run <code>pg_restore</code> manually.
        </CardContent>
      </Card>

      <Card>
        <CardHeader className="flex flex-row items-start justify-between gap-3 space-y-0">
          <div>
            <CardTitle className="text-base flex items-center gap-2">
              <HardDriveDownload className="h-4 w-4" aria-hidden />
              Full disaster-recovery export
            </CardTitle>
            <CardDescription>
              Bundles the database dump <em>and every stored file</em> (documents, berth PDFs,
              brochures, GDPR exports) into one <code>.tar</code> and downloads it to this computer.
              Use this as an offsite, storage-backend-independent backup.
            </CardDescription>
          </div>
          <Button variant="outline" onClick={downloadFullBundle}>
            <HardDriveDownload className="me-1.5 h-4 w-4" aria-hidden />
            Download full backup
          </Button>
        </CardHeader>
        <CardContent className="text-xs text-muted-foreground">
          The archive contains <code>db.dump</code>, <code>blobs/&lt;key&gt;</code> for every file,
          and a <code>manifest.json</code> with a SHA-256 per object for restore-side verification.
          Unlike the DB-only backup above, this does not depend on the active storage backend
          surviving. Restore steps live in <code>docs/backup-restore-runbook.md</code>.
        </CardContent>
      </Card>

      <Card>
        <CardHeader>
          <CardTitle className="text-base">History</CardTitle>
          <CardDescription>Last 50 backup jobs.</CardDescription>
        </CardHeader>
        <CardContent>
          {loading ? (
            <div className="flex items-center gap-2 text-sm text-muted-foreground">
              <Loader2 className="h-4 w-4 animate-spin" aria-hidden /> Loading…
            </div>
          ) : jobs.length === 0 ? (
            <p className="text-sm text-muted-foreground">No backups yet.</p>
          ) : (
            <ul className="divide-y">
              {jobs.map((j) => (
                <li key={j.id} className="flex items-center justify-between gap-3 py-2">
                  <div className="min-w-0 flex-1">
                    <div className="flex items-center gap-2">
                      <span
                        className={`text-xs uppercase font-medium px-1.5 py-0.5 rounded-full ${STATUS_TONE[j.status]}`}
                      >
                        {j.status}
                      </span>
                      <span className="text-xs text-muted-foreground">
                        {new Date(j.startedAt).toLocaleString()}
                      </span>
                      {j.trigger === 'cron' && (
                        <span className="text-xs text-muted-foreground">cron</span>
                      )}
                    </div>
                    {j.errorMessage && (
                      <p className="mt-1 text-xs text-rose-700 truncate">{j.errorMessage}</p>
                    )}
                  </div>
                  <div className="flex items-center gap-3 shrink-0">
                    <span className="text-xs text-muted-foreground">
                      {formatBytes(j.sizeBytes)}
                    </span>
                    {j.status === 'completed' && (
                      <Button size="sm" variant="outline" onClick={() => download(j.id)}>
                        <Download className="mr-1.5 h-3.5 w-3.5" aria-hidden />
                        Download
                      </Button>
                    )}
                  </div>
                </li>
              ))}
            </ul>
          )}
        </CardContent>
      </Card>

      <Dialog open={confirmOpen} onOpenChange={setConfirmOpen}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle className="flex items-center gap-2">
              <AlertTriangle className="h-5 w-5 text-amber-600" aria-hidden />
              Run a fresh backup now?
            </DialogTitle>
            <DialogDescription>
              This streams the entire database through <code>pg_dump</code> and stores the result in
              the active backend. On large databases the operation can take several minutes. The CRM
              stays online throughout.
            </DialogDescription>
          </DialogHeader>
          <DialogFooter>
            <Button variant="outline" onClick={() => setConfirmOpen(false)}>
              Cancel
            </Button>
            <Button onClick={trigger} disabled={running}>
              Run backup
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}
