import type { ServiceConnection } from '../../hooks/useServiceConnection';
import { useEffect, useState } from 'react';
import { CheckCircle2, Loader2, Plug, Trash2, TriangleAlert } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import {
  serviceErrorMessage,
  useServiceConnection,
  useServiceConnectionActions,
  useServiceHealth,
} from '../../hooks/useServiceConnection';

// Connecting a slskd daemon to Officer, from the app.
//
// Both the setup wizard and the permanent settings page: SoulseekView renders it in place of whatever
// section the nav asked for while nothing is connected, and the Connection section renders it for good.
// One component, so re-pointing at a different daemon later goes through exactly the code path that
// stored the first one.
//
// The API key is write-only — the GET it reads has no field that could carry one back, so the input is
// always blank and an empty input means "keep the stored key".

const HINT = 'text-[11px] leading-relaxed text-muted-foreground';

/** slskd's own default HTTP port. Officer dials it from the server, not from this browser. */
const DEFAULT_URL = 'http://localhost:5030';

const URL_HINT =
  "The daemon's base URL. Officer reaches it from the server, not from this browser — so localhost here " +
  'means the machine Officer runs on.';

const KEY_HINT =
  'An API key from slskd.yml (web.authentication.api_keys). Officer sends it as X-API-Key on every call.';

type SaveInput = Record<string, unknown> & { url: string };

export const SoulseekConnection = () => {
  const { data, isLoading } = useServiceConnection('slskd');
  const { data: health } = useServiceHealth('slskd');
  const { save, forget } = useServiceConnectionActions<SaveInput>('slskd');

  const connection = data?.connection ?? null;

  const [url, setUrl] = useState('');
  const [apiKey, setApiKey] = useState('');
  const [error, setError] = useState('');

  // Seed from the stored row once it arrives. Keyed on its id so a save (same id) doesn't stomp what the
  // owner is still typing, while forgetting and re-adding does reset the form.
  useEffect(() => {
    setUrl(connection?.url ?? '');
    setApiKey('');
  }, [connection?.id]);

  const submit = async () => {
    setError('');
    try {
      await save.mutateAsync({ url: url.trim(), apiKey: apiKey.trim() });
      setApiKey('');
    } catch (err) {
      setError(serviceErrorMessage(err));
    }
  };

  const remove = async () => {
    setError('');
    try {
      await forget.mutateAsync();
    } catch (err) {
      setError(serviceErrorMessage(err));
    }
  };

  if (isLoading) {
    return (
      <div className="flex h-full items-center justify-center text-sm text-muted-foreground">
        <Loader2 className="mr-2 h-4 w-4 animate-spin" /> Loading…
      </div>
    );
  }

  return (
    <div className="h-full overflow-y-auto">
      <div className="mx-auto flex max-w-xl flex-col gap-6 p-6">
        <header className="flex items-start gap-3">
          <div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-sky-500/15 text-sky-500">
            <Plug className="h-5 w-5" />
          </div>
          <div>
            <h2 className="text-sm font-semibold">slskd daemon</h2>
            <p className={HINT}>
              {connection
                ? 'Where Officer talks to slskd. Saving re-checks the daemon before storing anything.'
                : 'Point Officer at your slskd daemon. It needs the URL and one API key.'}
            </p>
          </div>
        </header>

        {connection && <StatusRow connection={connection} health={health} />}

        <div className="flex flex-col gap-4 rounded-xl border p-4">
          <label className="flex flex-col gap-1.5">
            <span className="text-xs font-medium">Server URL</span>
            <Input
              value={url}
              onChange={(ev) => setUrl(ev.target.value)}
              placeholder={DEFAULT_URL}
              autoFocus={!connection}
              autoComplete="off"
              spellCheck={false}
            />
            <span className={HINT}>{URL_HINT}</span>
          </label>

          <label className="flex flex-col gap-1.5">
            <span className="text-xs font-medium">API key</span>
            <Input
              value={apiKey}
              onChange={(ev) => setApiKey(ev.target.value)}
              placeholder={connection?.hasSecret ? '•••••••• (unchanged)' : 'slskd API key'}
              type="password"
              autoComplete="off"
              spellCheck={false}
            />
            <span className={HINT}>{KEY_HINT}</span>
          </label>

          {error && (
            <div className="flex items-start gap-2 rounded-lg bg-destructive/10 p-2.5 text-xs text-destructive">
              <TriangleAlert className="mt-px h-3.5 w-3.5 shrink-0" />
              <span>{error}</span>
            </div>
          )}

          <div className="flex items-center gap-2">
            <Button
              size="sm"
              onClick={submit}
              disabled={!url.trim() || (!apiKey.trim() && !connection?.hasSecret) || save.isPending}
            >
              {save.isPending && <Loader2 className="mr-2 h-3.5 w-3.5 animate-spin" />}
              {connection ? 'Save' : 'Connect'}
            </Button>
            {connection && (
              <Button size="sm" variant="ghost" onClick={remove} disabled={forget.isPending}>
                <Trash2 className="mr-2 h-3.5 w-3.5" />
                Disconnect
              </Button>
            )}
          </div>
        </div>
      </div>
    </div>
  );
};

type StatusRowProps = { connection: ServiceConnection; health: { ok: boolean; error?: string } | undefined };

const StatusRow = ({ connection, health }: StatusRowProps) => (
  <div className="flex items-start gap-2 rounded-xl border p-3 text-xs">
    {health?.ok ? (
      <CheckCircle2 className="mt-px h-4 w-4 shrink-0 text-emerald-500" />
    ) : (
      <TriangleAlert className="mt-px h-4 w-4 shrink-0 text-amber-500" />
    )}
    <div className="min-w-0">
      <div className="font-medium">{health?.ok ? 'Connected' : 'Not responding'}</div>
      <div className="truncate text-muted-foreground">
        {connection.url}
        {connection.version ? ` · slskd ${connection.version}` : ''}
      </div>
      {!health?.ok && health?.error && <div className="text-destructive">{health.error}</div>}
    </div>
  </div>
);
