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 Transmission daemon to Officer, from the app.
//
// This screen is BOTH the setup wizard and the permanent settings page: TransmissionView renders it in place
// of whatever section the URL asks for while nothing is connected, and /transmission/connection 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.
//
// ONE connection, not a registry — nobody runs two Transmission daemons. And usually one FIELD: most
// daemons run with no RPC auth at all, which is why username/password sit behind a disclosure rather than
// in the owner's way. An empty username means "no auth", not "empty credentials"; the sidecar is careful
// about that distinction because Transmission rejects a request carrying an empty Basic header.

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

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

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

const AUTH_HINT =
  'Only if the daemon has rpc-authentication-required set. Leave the username empty for the usual case: an ' +
  'empty username means no authentication, and sending a blank one anyway makes Transmission refuse the call.';

const PATH_HINT = 'Only differs behind a reverse proxy that mounts the RPC endpoint somewhere else.';

type FieldProps = {
  label: string;
  hint?: string;
  value: string;
  onChange: (value: string) => void;
  placeholder: string;
  type?: string;
  autoFocus?: boolean;
};

const Field = ({ label, hint, value, onChange, placeholder, type, autoFocus }: FieldProps) => (
  <label className="flex flex-col gap-1.5">
    <span className="text-xs font-medium">{label}</span>
    <Input
      value={value}
      onChange={(ev) => onChange(ev.target.value)}
      placeholder={placeholder}
      type={type}
      autoFocus={autoFocus}
      autoComplete="off"
      spellCheck={false}
    />
    {hint && <span className={HINT}>{hint}</span>}
  </label>
);

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

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

  const connection = data?.connection ?? null;

  const [url, setUrl] = useState('');
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [rpcPath, setRpcPath] = useState('');
  const [showAdvanced, setShowAdvanced] = useState(false);
  const [error, setError] = useState('');

  // Seed the form from the stored row once it arrives. Keyed on the row's id so a save (which returns the
  // same id) does not stomp what the owner is still typing, but forgetting and re-adding does reset it.
  useEffect(() => {
    setUrl(connection?.url ?? '');
    setUsername(connection?.username ?? '');
    setRpcPath(connection?.path ?? '');
    setPassword('');
    setShowAdvanced(!!connection?.username);
  }, [connection?.id]);

  const submit = async () => {
    setError('');
    try {
      await save.mutateAsync({ url: url.trim(), username: username.trim(), password, rpcPath: rpcPath.trim() });
      setPassword('');
    } 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-amber-500/15 text-amber-500">
            <Plug className="h-5 w-5" />
          </div>
          <div>
            <h2 className="text-sm font-semibold">Transmission daemon</h2>
            <p className={HINT}>
              {connection
                ? 'Where Officer talks to Transmission. Saving re-checks the daemon before storing anything.'
                : 'Point Officer at your Transmission daemon. Usually the URL is all it needs.'}
            </p>
          </div>
        </header>

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

        <div className="flex flex-col gap-4 rounded-xl border p-4">
          <Field
            label="Server URL"
            hint={URL_HINT}
            value={url}
            onChange={setUrl}
            placeholder={DEFAULT_URL}
            autoFocus={!connection}
          />

          <button
            type="button"
            onClick={() => setShowAdvanced((v) => !v)}
            className="self-start text-[11px] font-medium text-primary hover:underline"
          >
            {showAdvanced ? 'Hide' : 'Show'} authentication and RPC path
          </button>

          {showAdvanced && (
            <>
              <Field label="Username" hint={AUTH_HINT} value={username} onChange={setUsername} placeholder="(none)" />
              <Field
                label="Password"
                value={password}
                onChange={setPassword}
                placeholder={connection?.hasSecret ? '•••••••• (unchanged)' : '(none)'}
                type="password"
              />
              <Field
                label="RPC path"
                hint={PATH_HINT}
                value={rpcPath}
                onChange={setRpcPath}
                placeholder="/transmission/rpc"
              />
            </>
          )}

          {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() || 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 ? ` · Transmission ${connection.version}` : ''}
      </div>
      {!health?.ok && health?.error && <div className="text-destructive">{health.error}</div>}
    </div>
  </div>
);
