import { useState, useEffect, useCallback } from 'react';
import { toast } from 'sonner';
import { Copy, Trash2, Ban, Plus, Smartphone } from 'lucide-react';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button';
import { useClient } from 'hooks/useClient';
import { copyToClipboard } from 'helpers/clipboard';

// Per-device credentials for calendar and contacts sync (DAVx5, iOS, macOS, Thunderbird).
//
// The generated password is returned by POST and never again — it is stored as an argon2 hash, so there
// is nothing to read back. That single fact drives the whole design of this screen: the new credential
// is shown in a panel that stays put until dismissed, with the server URL and username beside it,
// because the moment it disappears the only remedy is to revoke and mint another.

type DavPassword = {
  id: number;
  label: string;
  hint: string;
  lastUsedAt: string | null;
  revokedAt: string | null;
  createdAt: string;
};

type Minted = { password: string; username: string; label: string };

const formatDate = (value: string | null) =>
  value ? new Date(value).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }) : null;

const copy = async (text: string, what: string) => {
  try {
    await copyToClipboard(text);
    toast.success(`${what} copied`);
  } catch {
    toast.error('Could not copy — select and copy manually');
  }
};

export const DavAppPasswords = () => {
  const client = useClient();
  const [passwords, setPasswords] = useState<DavPassword[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [label, setLabel] = useState('');
  const [isCreating, setIsCreating] = useState(false);
  const [minted, setMinted] = useState<Minted | null>(null);

  // The base the phone should be pointed at. Taken from the browser rather than configured: it is by
  // definition the address that reached this page, which is the one that will work.
  const serverUrl = typeof window !== 'undefined' ? window.location.origin : '';

  const load = useCallback(() => {
    client
      .get<{ passwords: DavPassword[] }>('/dav/passwords')
      .then((data) => setPasswords(data?.passwords ?? []))
      .catch(() => {})
      .finally(() => setIsLoading(false));
  }, [client]);

  useEffect(load, [load]);

  const create = async () => {
    const trimmed = label.trim();
    if (!trimmed || isCreating) return;
    setIsCreating(true);
    try {
      const res = await client.post<{ password: string; username: string }>('/dav/passwords', { label: trimmed });
      setMinted({ password: res.password, username: res.username, label: trimmed });
      setLabel('');
      load();
    } catch {
      toast.error('Could not create the app password');
    } finally {
      setIsCreating(false);
    }
  };

  const revoke = async (id: number) => {
    try {
      await client.post(`/dav/passwords/${id}/revoke`, {});
      toast.success('Revoked — that device will stop syncing');
      load();
    } catch {
      toast.error('Could not revoke');
    }
  };

  const remove = async (id: number) => {
    try {
      await client.delete(`/dav/passwords/${id}`);
      load();
    } catch {
      toast.error('Could not delete');
    }
  };

  if (isLoading) return null;

  return (
    <div className="grid gap-5">
      <p className="text-sm text-duck-dark/70 dark:text-foreground/70">
        Calendar and contacts sync uses one password per device. A phone can't hold an Officer session, so it gets its
        own credential — revocable on its own, without touching anything else.
      </p>

      {minted && (
        <div className="grid gap-3 rounded-lg border-2 border-duck-teal/40 bg-duck-teal/5 p-4">
          <div className="text-sm font-medium text-duck-dark dark:text-foreground">
            Password for “{minted.label}” — shown once
          </div>
          <p className="text-xs text-duck-dark/60 dark:text-foreground/60">
            This is the only time it is displayed. It's stored hashed, so it can't be shown again — if you lose it,
            revoke this entry and make another.
          </p>

          <div className="grid gap-2">
            {[
              { field: 'Server', value: serverUrl },
              { field: 'Username', value: minted.username },
              { field: 'Password', value: minted.password },
            ].map(({ field, value }) => (
              <div key={field} className="flex items-center gap-2">
                <span className="w-20 shrink-0 text-xs text-duck-dark/50 dark:text-foreground/50">{field}</span>
                <code className="min-w-0 flex-1 truncate rounded bg-background/70 px-2 py-1.5 font-mono text-xs">
                  {value}
                </code>
                <Button variant="ghost" size="sm" onClick={() => copy(value, field)} title={`Copy ${field}`}>
                  <Copy className="h-3.5 w-3.5" />
                </Button>
              </div>
            ))}
          </div>

          <p className="text-xs text-duck-dark/60 dark:text-foreground/60">
            In DAVx5 or iOS, choose “login with URL and username” and give it the server address above — not a full
            calendar path. Discovery does the rest.
          </p>

          <Button variant="outline" size="sm" className="justify-self-start" onClick={() => setMinted(null)}>
            I've saved it
          </Button>
        </div>
      )}

      <div className="flex items-end gap-2">
        <Label className="grid flex-1 gap-2">
          <span className="text-duck-dark/70 dark:text-foreground/70">New device</span>
          <Input
            className="h-11 bg-background/60 border-duck-dark/20"
            value={label}
            onChange={(ev) => setLabel(ev.target.value)}
            onKeyDown={(ev) => ev.key === 'Enter' && create()}
            placeholder="Pixel 9, iPad, Thunderbird…"
          />
        </Label>
        <Button onClick={create} disabled={!label.trim() || isCreating} className="h-11">
          <Plus className="mr-1.5 h-4 w-4" />
          Create
        </Button>
      </div>

      {passwords.length === 0 ? (
        <div className="rounded-lg border border-dashed border-duck-dark/15 dark:border-foreground/15 p-6 text-center text-sm text-duck-dark/40 dark:text-foreground/40">
          No devices yet.
        </div>
      ) : (
        <div className="grid gap-2">
          {passwords.map((entry) => {
            const revoked = !!entry.revokedAt;
            const lastUsed = formatDate(entry.lastUsedAt);
            return (
              <div
                key={entry.id}
                className={`flex items-center gap-3 rounded-lg border p-3 ${
                  revoked
                    ? 'border-duck-dark/10 dark:border-foreground/10 opacity-50'
                    : 'border-duck-dark/15 dark:border-foreground/15'
                }`}
              >
                <Smartphone className="h-4 w-4 shrink-0 text-duck-teal/70" />
                <div className="min-w-0 flex-1">
                  <div className="truncate text-sm text-duck-dark dark:text-foreground">
                    {entry.label}
                    {revoked && <span className="ml-2 text-xs text-red-500">revoked</span>}
                  </div>
                  <div className="text-xs text-duck-dark/40 dark:text-foreground/40">
                    <code className="font-mono">{entry.hint}…</code>
                    {' · '}
                    {/* "never used" is the tell that a device was set up wrong, so it earns its own wording. */}
                    {lastUsed ? `last used ${lastUsed}` : 'never used'}
                  </div>
                </div>
                {!revoked && (
                  <Button variant="ghost" size="sm" onClick={() => revoke(entry.id)} title="Revoke">
                    <Ban className="h-3.5 w-3.5" />
                  </Button>
                )}
                <Button
                  variant="ghost"
                  size="sm"
                  onClick={() => remove(entry.id)}
                  title="Delete"
                  className="hover:text-red-500"
                >
                  <Trash2 className="h-3.5 w-3.5" />
                </Button>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
};
