'use client';

import { useEffect, useState } from 'react';
import {
  Loader2,
  Plus,
  Server,
  Cloud,
  FolderTree,
  Trash2,
  Pencil,
  PlugZap,
  UploadCloud,
  ShieldCheck,
} 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 { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';
import { apiFetch } from '@/lib/api/client';
import { toastError } from '@/lib/api/toast-error';

type DestType = 'sftp' | 's3' | 'filesystem';
type Schedule = 'off' | 'daily' | 'weekly';

interface Destination {
  id: string;
  name: string;
  type: DestType;
  enabled: boolean;
  config: Record<string, unknown>;
  retentionCount: number | null;
  encryptBundle: boolean;
  encryptionKeyIsSet: boolean;
  lastRunAt: string | null;
  lastStatus: string | null;
  lastError: string | null;
  lastBackupBytes: number | null;
}

const TYPE_META: Record<DestType, { label: string; icon: typeof Server; hint: string }> = {
  sftp: { label: 'SFTP / SSH server', icon: Server, hint: 'Push to a server over SFTP.' },
  s3: { label: 'S3-compatible', icon: Cloud, hint: 'AWS S3, Backblaze B2, Wasabi, R2, MinIO.' },
  filesystem: {
    label: 'Mounted path / NAS',
    icon: FolderTree,
    hint: 'A directory this server can write to.',
  },
};

const STATUS_TONE: Record<string, string> = {
  ok: 'text-emerald-700',
  failed: 'text-rose-700',
};

function formatBytes(n: number | null): string {
  if (n === null) return '—';
  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 BackupDestinationsCard() {
  const [destinations, setDestinations] = useState<Destination[]>([]);
  const [schedule, setSchedule] = useState<Schedule>('off');
  const [loading, setLoading] = useState(true);
  const [busyId, setBusyId] = useState<string | null>(null);
  const [editing, setEditing] = useState<Destination | 'new' | null>(null);

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

  async function load() {
    setLoading(true);
    try {
      const [d, s] = await Promise.all([
        apiFetch<{ data: Destination[] }>('/api/v1/admin/backup/destinations'),
        apiFetch<{ data: { schedule: Schedule } }>('/api/v1/admin/backup/schedule'),
      ]);
      setDestinations(d.data);
      setSchedule(s.data.schedule);
    } catch (err) {
      toastError(err);
    } finally {
      setLoading(false);
    }
  }

  async function changeSchedule(value: Schedule) {
    setSchedule(value);
    try {
      await apiFetch('/api/v1/admin/backup/schedule', {
        method: 'PUT',
        body: { schedule: value },
      });
      toast.success(
        value === 'off' ? 'Automated backups turned off' : `Automated backups: ${value}`,
      );
    } catch (err) {
      toastError(err);
      void load();
    }
  }

  async function test(id: string) {
    setBusyId(id);
    try {
      await apiFetch(`/api/v1/admin/backup/destinations/${id}/test`, { method: 'POST' });
      toast.success('Connection OK');
    } catch (err) {
      toastError(err);
    } finally {
      setBusyId(null);
    }
  }

  async function runNow(id: string) {
    setBusyId(id);
    try {
      toast.info('Backing up — assembling the bundle, then pushing. This can take a minute.');
      await apiFetch(`/api/v1/admin/backup/destinations/${id}/run`, { method: 'POST' });
      toast.success('Backup pushed');
      await load();
    } catch (err) {
      toastError(err);
      await load();
    } finally {
      setBusyId(null);
    }
  }

  async function remove(id: string) {
    if (!confirm('Delete this backup destination? This does not delete already-pushed backups.'))
      return;
    setBusyId(id);
    try {
      await apiFetch(`/api/v1/admin/backup/destinations/${id}`, { method: 'DELETE' });
      toast.success('Destination deleted');
      await load();
    } catch (err) {
      toastError(err);
    } finally {
      setBusyId(null);
    }
  }

  return (
    <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">
            <UploadCloud className="h-4 w-4" aria-hidden />
            Automated backup destinations
          </CardTitle>
          <CardDescription>
            Where scheduled backups are pushed. Each destination receives the same full bundle
            (database + every file) you can download above.
          </CardDescription>
        </div>
        <Button size="sm" onClick={() => setEditing('new')}>
          <Plus className="me-1.5 h-4 w-4" aria-hidden />
          Add destination
        </Button>
      </CardHeader>
      <CardContent className="space-y-4">
        <div className="flex items-center gap-3">
          <Label htmlFor="backup-schedule" className="text-sm">
            Schedule
          </Label>
          <Select value={schedule} onValueChange={(v) => void changeSchedule(v as Schedule)}>
            <SelectTrigger id="backup-schedule" className="w-44">
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="off">Off</SelectItem>
              <SelectItem value="daily">Daily (02:00)</SelectItem>
              <SelectItem value="weekly">Weekly (Sun 02:00)</SelectItem>
            </SelectContent>
          </Select>
          <span className="text-xs text-muted-foreground">
            Pushes to every enabled destination below.
          </span>
        </div>

        {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>
        ) : destinations.length === 0 ? (
          <p className="text-sm text-muted-foreground">
            No destinations yet. Add one to enable automated offsite backups.
          </p>
        ) : (
          <ul className="divide-y rounded-md border">
            {destinations.map((d) => {
              const Icon = TYPE_META[d.type].icon;
              return (
                <li key={d.id} className="flex items-center justify-between gap-3 p-3">
                  <div className="min-w-0 flex-1">
                    <div className="flex items-center gap-2">
                      <Icon className="h-4 w-4 text-muted-foreground" aria-hidden />
                      <span className="font-medium truncate">{d.name}</span>
                      {d.encryptBundle && (
                        <ShieldCheck
                          className="h-3.5 w-3.5 text-emerald-600"
                          aria-label="Encrypted"
                        />
                      )}
                      {!d.enabled && (
                        <span className="text-xs rounded-full bg-muted px-1.5 py-0.5 text-muted-foreground">
                          disabled
                        </span>
                      )}
                    </div>
                    <div className="mt-0.5 text-xs text-muted-foreground">
                      {TYPE_META[d.type].label}
                      {d.lastStatus && (
                        <>
                          {' · '}
                          <span className={STATUS_TONE[d.lastStatus] ?? ''}>
                            {d.lastStatus === 'ok' ? 'last OK' : 'last FAILED'}
                          </span>
                          {d.lastRunAt && ` ${new Date(d.lastRunAt).toLocaleString()}`}
                          {d.lastStatus === 'ok' && ` (${formatBytes(d.lastBackupBytes)})`}
                        </>
                      )}
                      {d.lastStatus === 'failed' && d.lastError && (
                        <span className="block text-rose-700 truncate">{d.lastError}</span>
                      )}
                    </div>
                  </div>
                  <div className="flex shrink-0 items-center gap-1.5">
                    <Button
                      size="sm"
                      variant="ghost"
                      disabled={busyId === d.id}
                      onClick={() => void test(d.id)}
                    >
                      <PlugZap className="me-1 h-3.5 w-3.5" aria-hidden />
                      Test
                    </Button>
                    <Button
                      size="sm"
                      variant="outline"
                      disabled={busyId === d.id}
                      onClick={() => void runNow(d.id)}
                    >
                      {busyId === d.id ? (
                        <Loader2 className="me-1 h-3.5 w-3.5 animate-spin" aria-hidden />
                      ) : (
                        <UploadCloud className="me-1 h-3.5 w-3.5" aria-hidden />
                      )}
                      Back up now
                    </Button>
                    <Button size="icon" variant="ghost" onClick={() => setEditing(d)}>
                      <Pencil className="h-3.5 w-3.5" aria-hidden />
                    </Button>
                    <Button
                      size="icon"
                      variant="ghost"
                      disabled={busyId === d.id}
                      onClick={() => void remove(d.id)}
                    >
                      <Trash2 className="h-3.5 w-3.5 text-rose-600" aria-hidden />
                    </Button>
                  </div>
                </li>
              );
            })}
          </ul>
        )}
      </CardContent>

      {editing && (
        <DestinationDialog
          destination={editing === 'new' ? null : editing}
          onClose={() => setEditing(null)}
          onSaved={() => {
            setEditing(null);
            void load();
          }}
        />
      )}
    </Card>
  );
}

// ─── add/edit dialog ─────────────────────────────────────────────────────────

interface DialogProps {
  destination: Destination | null;
  onClose: () => void;
  onSaved: () => void;
}

function DestinationDialog({ destination, onClose, onSaved }: DialogProps) {
  const isEdit = Boolean(destination);
  const cfg = (destination?.config ?? {}) as Record<string, unknown>;
  const str = (k: string) => (typeof cfg[k] === 'string' ? (cfg[k] as string) : '');
  const num = (k: string) => (typeof cfg[k] === 'number' ? String(cfg[k]) : '');

  const [name, setName] = useState(destination?.name ?? '');
  const [type, setType] = useState<DestType>(destination?.type ?? 'sftp');
  const [enabled, setEnabled] = useState(destination?.enabled ?? true);
  const [retention, setRetention] = useState(
    destination?.retentionCount != null ? String(destination.retentionCount) : '',
  );
  const [encryptBundle, setEncryptBundle] = useState(destination?.encryptBundle ?? false);
  const [encryptionKey, setEncryptionKey] = useState('');
  const [saving, setSaving] = useState(false);

  // Config fields (controlled). Secrets start blank on edit (kept server-side).
  const [c, setC] = useState<Record<string, string>>({
    directory: str('directory'),
    host: str('host'),
    port: num('port'),
    username: str('username'),
    password: '',
    privateKey: '',
    passphrase: '',
    remoteDir: str('remoteDir'),
    hostFingerprint: str('hostFingerprint'),
    endpoint: str('endpoint'),
    region: str('region'),
    bucket: str('bucket'),
    accessKey: str('accessKey'),
    secretKey: '',
    prefix: str('prefix'),
  });
  const set = (k: string, v: string) => setC((prev) => ({ ...prev, [k]: v }));

  function buildConfig(): Record<string, unknown> {
    if (type === 'filesystem') return { directory: c.directory };
    if (type === 'sftp') {
      return {
        host: c.host,
        ...(c.port ? { port: Number(c.port) } : {}),
        username: c.username,
        ...(c.password ? { password: c.password } : {}),
        ...(c.privateKey ? { privateKey: c.privateKey } : {}),
        ...(c.passphrase ? { passphrase: c.passphrase } : {}),
        remoteDir: c.remoteDir,
        ...(c.hostFingerprint ? { hostFingerprint: c.hostFingerprint } : {}),
      };
    }
    return {
      endpoint: c.endpoint,
      ...(c.region ? { region: c.region } : {}),
      bucket: c.bucket,
      accessKey: c.accessKey,
      ...(c.secretKey ? { secretKey: c.secretKey } : {}),
      ...(c.prefix ? { prefix: c.prefix } : {}),
    };
  }

  async function save() {
    setSaving(true);
    try {
      const body = {
        name,
        type,
        enabled,
        config: buildConfig(),
        retentionCount: retention ? Number(retention) : null,
        encryptBundle,
        ...(encryptionKey ? { encryptionKey } : {}),
      };
      if (isEdit && destination) {
        await apiFetch(`/api/v1/admin/backup/destinations/${destination.id}`, {
          method: 'PUT',
          body,
        });
      } else {
        await apiFetch('/api/v1/admin/backup/destinations', {
          method: 'POST',
          body,
        });
      }
      toast.success(isEdit ? 'Destination updated' : 'Destination added');
      onSaved();
    } catch (err) {
      toastError(err);
    } finally {
      setSaving(false);
    }
  }

  const secretPlaceholder = isEdit ? 'unchanged — leave blank to keep' : '';

  return (
    <Dialog open onOpenChange={(o) => !o && onClose()}>
      <DialogContent className="max-h-[85vh] overflow-y-auto sm:max-w-lg">
        <DialogHeader>
          <DialogTitle>{isEdit ? 'Edit destination' : 'Add backup destination'}</DialogTitle>
          <DialogDescription>{TYPE_META[type].hint}</DialogDescription>
        </DialogHeader>

        <div className="space-y-3">
          <Field label="Name">
            <Input
              value={name}
              onChange={(e) => setName(e.target.value)}
              placeholder="Hetzner box"
            />
          </Field>
          <Field label="Type">
            <Select value={type} onValueChange={(v) => setType(v as DestType)} disabled={isEdit}>
              <SelectTrigger>
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="sftp">SFTP / SSH server</SelectItem>
                <SelectItem value="s3">S3-compatible</SelectItem>
                <SelectItem value="filesystem">Mounted path / NAS</SelectItem>
              </SelectContent>
            </Select>
          </Field>

          {type === 'filesystem' && (
            <Field label="Directory">
              <Input
                value={c.directory}
                onChange={(e) => set('directory', e.target.value)}
                placeholder="/mnt/backups"
              />
            </Field>
          )}

          {type === 'sftp' && (
            <>
              <div className="grid grid-cols-3 gap-2">
                <div className="col-span-2">
                  <Field label="Host">
                    <Input
                      value={c.host}
                      onChange={(e) => set('host', e.target.value)}
                      placeholder="backups.example.com"
                    />
                  </Field>
                </div>
                <Field label="Port">
                  <Input
                    value={c.port}
                    onChange={(e) => set('port', e.target.value)}
                    placeholder="22"
                  />
                </Field>
              </div>
              <Field label="Username">
                <Input value={c.username} onChange={(e) => set('username', e.target.value)} />
              </Field>
              <Field label="Password">
                <Input
                  type="password"
                  value={c.password}
                  onChange={(e) => set('password', e.target.value)}
                  placeholder={secretPlaceholder}
                />
              </Field>
              <Field label="Private key (optional, instead of password)">
                <textarea
                  className="w-full rounded-md border px-2 py-1.5 text-xs font-mono"
                  rows={3}
                  value={c.privateKey}
                  onChange={(e) => set('privateKey', e.target.value)}
                  placeholder={secretPlaceholder || '-----BEGIN OPENSSH PRIVATE KEY-----'}
                />
              </Field>
              <Field label="Remote directory">
                <Input
                  value={c.remoteDir}
                  onChange={(e) => set('remoteDir', e.target.value)}
                  placeholder="/srv/pn-crm-backups"
                />
              </Field>
              <Field label="Host key fingerprint (optional, sha256 — pins the server)">
                <Input
                  value={c.hostFingerprint}
                  onChange={(e) => set('hostFingerprint', e.target.value)}
                  placeholder="aa:bb:cc… or hex"
                />
              </Field>
            </>
          )}

          {type === 's3' && (
            <>
              <Field label="Endpoint">
                <Input
                  value={c.endpoint}
                  onChange={(e) => set('endpoint', e.target.value)}
                  placeholder="https://s3.us-west.example.com"
                />
              </Field>
              <div className="grid grid-cols-2 gap-2">
                <Field label="Bucket">
                  <Input value={c.bucket} onChange={(e) => set('bucket', e.target.value)} />
                </Field>
                <Field label="Region (optional)">
                  <Input
                    value={c.region}
                    onChange={(e) => set('region', e.target.value)}
                    placeholder="us-east-1"
                  />
                </Field>
              </div>
              <Field label="Access key">
                <Input value={c.accessKey} onChange={(e) => set('accessKey', e.target.value)} />
              </Field>
              <Field label="Secret key">
                <Input
                  type="password"
                  value={c.secretKey}
                  onChange={(e) => set('secretKey', e.target.value)}
                  placeholder={secretPlaceholder}
                />
              </Field>
              <Field label="Prefix (optional)">
                <Input
                  value={c.prefix}
                  onChange={(e) => set('prefix', e.target.value)}
                  placeholder="crm-backups/"
                />
              </Field>
            </>
          )}

          <div className="grid grid-cols-2 gap-2">
            <Field label="Keep last N (blank = all)">
              <Input
                value={retention}
                onChange={(e) => setRetention(e.target.value)}
                placeholder="7"
              />
            </Field>
            <div className="flex items-end pb-1">
              <label className="flex items-center gap-2 text-sm">
                <Switch checked={enabled} onCheckedChange={setEnabled} />
                Enabled (auto-pushed)
              </label>
            </div>
          </div>

          <div className="rounded-md border p-3 space-y-2">
            <label className="flex items-center gap-2 text-sm">
              <Switch checked={encryptBundle} onCheckedChange={setEncryptBundle} />
              Encrypt the bundle before sending (AES-256)
            </label>
            {encryptBundle && (
              <Field label="Passphrase (needed to restore — store it safely)">
                <Input
                  type="password"
                  value={encryptionKey}
                  onChange={(e) => setEncryptionKey(e.target.value)}
                  placeholder={
                    destination?.encryptionKeyIsSet ? 'unchanged — leave blank to keep' : ''
                  }
                />
              </Field>
            )}
          </div>
        </div>

        <DialogFooter>
          <Button variant="outline" onClick={onClose} disabled={saving}>
            Cancel
          </Button>
          <Button onClick={() => void save()} disabled={saving || !name}>
            {saving && <Loader2 className="me-1.5 h-4 w-4 animate-spin" aria-hidden />}
            {isEdit ? 'Save changes' : 'Add destination'}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

function Field({ label, children }: { label: string; children: React.ReactNode }) {
  return (
    <div className="space-y-1">
      <Label className="text-xs text-muted-foreground">{label}</Label>
      {children}
    </div>
  );
}
