import { useState } from 'react';
import { KeyRound, Plus, Trash2, TimerOff, Copy, Check, Loader2, ShieldAlert } from 'lucide-react';
import type { HeadscalePreAuthKey } from './shared';
import { useHeadscaleKeys, useHeadscaleUsers } from './useHeadscaleData';
import { useHeadscaleServers, headscaleErrorMessage } from './useHeadscaleServers';
import { timeAgo, timeUntil, fullDate } from './format';
import { Card, Button, Field, Badge, Dot, ErrorNote } from './Cards';
import { ViewShell, EmptyBody } from './ViewShell';
import { copyToClipboard } from 'helpers/clipboard';

// Pre-auth keys — the tokens a machine presents to join the tailnet.
//
// The whole screen is shaped by one fact: Headscale hashes keys, so the secret exists exactly once, in the
// create response. If this view renders it into a list, or lets a re-render drop it, the key is gone and the
// owner has to make another. Hence the show-once panel: it holds the secret in local state, offers the copy
// and the ready-to-paste join command, and only clears on an explicit dismiss.
//
// The list defaults to active keys because a long-lived server accumulates hundreds of spent ones.

const STATUS_FILTERS = [
  { id: 'active', label: 'Active' },
  { id: 'all', label: 'All' },
] as const;

type StatusFilter = (typeof STATUS_FILTERS)[number]['id'];

const STATUS_TONE = { active: 'ok', used: 'idle', expired: 'bad' } as const;

const CopyButton = ({ value, label }: { value: string; label: string }) => {
  const [done, setDone] = useState(false);
  const copy = () => {
    void copyToClipboard(value);
    setDone(true);
    window.setTimeout(() => setDone(false), 1500);
  };
  return (
    <Button onClick={copy} variant={done ? 'primary' : 'ghost'}>
      {done ? <Check className="h-3.5 w-3.5" /> : <Copy className="h-3.5 w-3.5" />}
      {done ? 'Copied' : label}
    </Button>
  );
};

type SecretPanelProps = { secret: string; loginServer: string; onDismiss: () => void };

const SecretPanel = ({ secret, loginServer, onDismiss }: SecretPanelProps) => {
  const command = `tailscale up --login-server ${loginServer} --authkey ${secret}`;
  return (
    <div className="overflow-hidden rounded-xl border border-amber-500/30 bg-amber-500/[0.07]">
      <div className="flex items-start gap-2.5 border-b border-amber-500/20 px-4 py-3">
        <ShieldAlert className="mt-0.5 h-4 w-4 shrink-0 text-amber-400" />
        <div className="min-w-0">
          <div className="text-sm font-semibold text-amber-200">Copy this key now</div>
          <p className="mt-0.5 text-xs leading-snug text-amber-200/70">
            Headscale stores it hashed. Once you dismiss this, nothing — not Officer, not the server — can show it
            again.
          </p>
        </div>
      </div>
      <div className="flex flex-col gap-3 p-4">
        <div>
          <div className="pb-1.5 text-[10px] font-semibold uppercase tracking-wide text-amber-200/60">Key</div>
          <div className="rounded-lg border border-white/10 bg-black/50 px-3 py-2 font-mono text-xs break-all text-zinc-100">
            {secret}
          </div>
        </div>
        <div>
          <div className="pb-1.5 text-[10px] font-semibold uppercase tracking-wide text-amber-200/60">Join command</div>
          <div className="rounded-lg border border-white/10 bg-black/50 px-3 py-2 font-mono text-xs break-all text-zinc-400">
            {command}
          </div>
        </div>
        <div className="flex flex-wrap items-center gap-2">
          <CopyButton value={secret} label="Copy key" />
          <CopyButton value={command} label="Copy command" />
          <Button variant="danger" onClick={onDismiss}>
            I've saved it
          </Button>
        </div>
      </div>
    </div>
  );
};

type ToggleProps = { checked: boolean; onChange: (value: boolean) => void; label: string; hint: string };

const Toggle = ({ checked, onChange, label, hint }: ToggleProps) => (
  <label className="flex cursor-pointer items-start gap-2.5">
    <input
      type="checkbox"
      checked={checked}
      onChange={(ev) => onChange(ev.target.checked)}
      className="mt-0.5 h-3.5 w-3.5 cursor-pointer accent-primary"
    />
    <span className="min-w-0">
      <span className="block text-xs font-medium text-zinc-300">{label}</span>
      <span className="block text-[11px] leading-snug text-zinc-600">{hint}</span>
    </span>
  </label>
);

type CreateKeyFormProps = { onCreated: (secret: string) => void; onClose: () => void };

const CreateKeyForm = ({ onCreated, onClose }: CreateKeyFormProps) => {
  const { users } = useHeadscaleUsers();
  const { create } = useHeadscaleKeys();
  const [userId, setUserId] = useState('');
  const [reusable, setReusable] = useState(false);
  const [ephemeral, setEphemeral] = useState(false);
  const [days, setDays] = useState('90');
  const [tags, setTags] = useState('');
  const [error, setError] = useState<string | null>(null);

  const submit = async () => {
    setError(null);
    const chosen = userId || users[0]?.id;
    if (!chosen) return setError('Create a user first — every key belongs to one.');
    const expirationDays = Number(days);
    if (!Number.isFinite(expirationDays) || expirationDays <= 0)
      return setError('Expiry must be a positive number of days');

    try {
      const result = await create.mutateAsync({
        userId: chosen,
        reusable,
        ephemeral,
        expirationDays,
        aclTags: tags
          .split(',')
          .map((t) => t.trim())
          .filter(Boolean),
      });
      if (result.key.key) onCreated(result.key.key);
      onClose();
    } catch (err) {
      setError(headscaleErrorMessage(err));
    }
  };

  return (
    <Card>
      <form
        onSubmit={(ev) => {
          ev.preventDefault();
          void submit();
        }}
        className="flex flex-col gap-3 p-4"
      >
        <div className="text-sm font-semibold text-zinc-100">New pre-auth key</div>

        <label className="flex flex-col gap-1.5">
          <span className="text-xs font-medium text-zinc-400">User</span>
          <select
            value={userId || users[0]?.id || ''}
            onChange={(ev) => setUserId(ev.target.value)}
            className="rounded-lg border border-white/10 bg-black/40 px-3 py-2 text-sm text-zinc-100 outline-none focus:border-primary/50"
          >
            {users.map((user) => (
              <option key={user.id} value={user.id}>
                {user.name}
              </option>
            ))}
          </select>
        </label>

        <div className="flex flex-col gap-2">
          <Toggle
            checked={reusable}
            onChange={setReusable}
            label="Reusable"
            hint="Any number of machines can join with it, until it expires."
          />
          <Toggle
            checked={ephemeral}
            onChange={setEphemeral}
            label="Ephemeral"
            hint="Nodes that join with it are removed when they go offline. For containers and CI."
          />
        </div>

        <Field label="Expires in (days)" value={days} onChange={setDays} placeholder="90" />
        <Field
          label="ACL tags (optional)"
          value={tags}
          onChange={setTags}
          placeholder="server, ci"
          hint="Comma separated. The tag: prefix is added for you."
        />

        {error && <ErrorNote>{error}</ErrorNote>}

        <div className="flex items-center gap-2 pt-1">
          <Button type="submit" variant="primary" disabled={create.isPending}>
            {create.isPending && <Loader2 className="h-3.5 w-3.5 animate-spin" />}
            Create key
          </Button>
          <Button onClick={onClose} disabled={create.isPending}>
            Cancel
          </Button>
        </div>
      </form>
    </Card>
  );
};

type KeyRowProps = { entry: HeadscalePreAuthKey; onError: (message: string) => void };

const KeyRow = ({ entry, onError }: KeyRowProps) => {
  const { expire, remove } = useHeadscaleKeys();
  const [confirming, setConfirming] = useState(false);
  const busy = expire.isPending || remove.isPending;

  const run = async (fn: () => Promise<unknown>) => {
    try {
      await fn();
    } catch (err) {
      onError(headscaleErrorMessage(err));
    }
  };

  return (
    <Card>
      <div className="flex flex-wrap items-center gap-3 p-3.5">
        <Dot tone={STATUS_TONE[entry.status]} />
        <div className="min-w-0 flex-1">
          <div className="flex flex-wrap items-center gap-2">
            <span className="truncate font-mono text-xs text-zinc-300">{entry.keyDisplay}</span>
            {entry.user && <Badge>{entry.user.name}</Badge>}
            {entry.reusable && <Badge>reusable</Badge>}
            {entry.ephemeral && <Badge>ephemeral</Badge>}
            {entry.aclTags.map((tag) => (
              <Badge key={tag} tone="active">
                {tag}
              </Badge>
            ))}
          </div>
          <div className="mt-0.5 flex flex-wrap items-center gap-x-2 text-[11px] text-zinc-500">
            <span>{entry.status}</span>
            <span title={fullDate(entry.expiration)}>· expires {timeUntil(entry.expiration)}</span>
            <span>· created {timeAgo(entry.createdAt)}</span>
          </div>
        </div>

        <div className="flex shrink-0 flex-wrap items-center gap-2">
          {entry.status === 'active' && (
            <Button onClick={() => void run(() => expire.mutateAsync(entry.id))} disabled={busy} title="Expire now">
              <TimerOff className="h-3.5 w-3.5" />
              Expire
            </Button>
          )}
          {confirming ? (
            <>
              <Button variant="danger" onClick={() => void run(() => remove.mutateAsync(entry.id))} disabled={busy}>
                <Trash2 className="h-3.5 w-3.5" />
                Confirm delete
              </Button>
              <Button onClick={() => setConfirming(false)} disabled={busy}>
                Cancel
              </Button>
            </>
          ) : (
            <Button variant="danger" onClick={() => setConfirming(true)} disabled={busy}>
              <Trash2 className="h-3.5 w-3.5" />
              Delete
            </Button>
          )}
        </div>
      </div>
    </Card>
  );
};

export const KeysView = () => {
  const { keys, isLoading, error } = useHeadscaleKeys();
  const { active } = useHeadscaleServers();
  const [creating, setCreating] = useState(false);
  const [secret, setSecret] = useState<string | null>(null);
  const [filter, setFilter] = useState<StatusFilter>('active');
  const [actionError, setActionError] = useState<string | null>(null);

  const visible = filter === 'all' ? keys : keys.filter((k) => k.status === 'active');
  const activeCount = keys.filter((k) => k.status === 'active').length;

  return (
    <ViewShell isLoading={isLoading} error={error} label="pre-auth keys">
      <div className="mx-auto flex w-full max-w-2xl flex-col gap-3">
        <div className="flex items-start justify-between gap-4 px-1 pb-1">
          <div className="min-w-0">
            <h2 className="text-sm font-semibold text-zinc-100">Pre-auth keys</h2>
            <p className="mt-0.5 text-xs text-zinc-500">
              {activeCount} active of {keys.length}
            </p>
          </div>
          <div className="flex shrink-0 items-center gap-2">
            <div className="flex items-center gap-0.5 rounded-lg border border-white/10 p-0.5">
              {STATUS_FILTERS.map((option) => (
                <button
                  key={option.id}
                  type="button"
                  onClick={() => setFilter(option.id)}
                  className={`cursor-pointer rounded-md px-2 py-1 text-[11px] transition-colors ${
                    filter === option.id ? 'bg-white/10 text-zinc-100' : 'text-zinc-500 hover:text-zinc-300'
                  }`}
                >
                  {option.label}
                </button>
              ))}
            </div>
            {!creating && (
              <Button variant="primary" onClick={() => setCreating(true)}>
                <Plus className="h-3.5 w-3.5" />
                New key
              </Button>
            )}
          </div>
        </div>

        {secret && <SecretPanel secret={secret} loginServer={active?.url ?? ''} onDismiss={() => setSecret(null)} />}
        {creating && <CreateKeyForm onCreated={setSecret} onClose={() => setCreating(false)} />}
        {actionError && <ErrorNote>{actionError}</ErrorNote>}

        {keys.length === 0 && !creating && (
          <EmptyBody
            icon={<KeyRound className="h-6 w-6" />}
            title="No pre-auth keys"
            hint="A key lets a machine join without an interactive login. Create one, then run the join command it gives you."
          />
        )}
        {keys.length > 0 && visible.length === 0 && (
          <div className="py-10 text-center text-sm text-zinc-500">
            No active keys. Switch to “All” to see spent and expired ones.
          </div>
        )}

        {visible.map((entry) => (
          <KeyRow key={entry.id} entry={entry} onError={setActionError} />
        ))}
      </div>
    </ViewShell>
  );
};
