import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { HelpText } from './HelpText';
import { EnvOverrideWarning, FieldLabel, FieldInput } from './formUtils';
import { SecretInput } from './SecretInput';
import { ModelSelect } from './ModelSelect';
import { StringArrayEditor } from './StringArrayEditor';
import type { SectionFormProps } from './types';

/**
 * Worker entry shape used by the v2 `llm.workers[]` config block. The
 * field names mirror what the server expects after camelCase
 * conversion (see src/config.ts:transformKeys). The runtime AppConfig
 * still uses `provider.workers` internally during the v1→v2 compat
 * window, but the API surface and this UI are v2-only.
 */
interface LlmWorker {
  id?: string;
  connectionType?: 'direct' | 'aao_gateway';
  endpoint?: string;
  apiKey?: string;
  model?: string;
  roles?: string[];
  maxConcurrency?: number;
  enabled?: boolean;
  vlm?: boolean;
  /**
   * Phase 1 compat: older `provider.workers[].proxy: true` rows are
   * mapped to `connectionType: aao_gateway` by the normalizer. We
   * still surface the field name so the UI can read legacy drafts
   * that haven't been migrated yet.
   */
  proxy?: boolean;
}

interface LlmConfigShape {
  timeoutMinutes?: number;
  retry?: {
    maxAttempts?: number;
    backoffMs?: number[];
    retryableStatus?: number[];
  };
  workers?: LlmWorker[];
}

/**
 * Detect whether an `aao_gateway` worker's endpoint appears to point at
 * the current AAO instance itself. Heuristic only — reverse proxies
 * and deployment-specific hostnames can defeat this, so we never block
 * save; the warning is purely an "are you sure?" hint.
 *
 * Triggers when the endpoint host is:
 *   - `localhost` / `127.0.0.1` / `::1`
 *   - the same host as `window.location.host` (excluding port mismatch
 *     — a separate gateway process on the same box is legitimate)
 *
 * Phase 2 (out of scope for this PR) will replace this with a hard
 * UUID check against `/aao/instance-id`.
 */
function detectSelfLoop(endpoint: string | undefined): boolean {
  if (!endpoint) return false;
  let url: URL;
  try {
    url = new URL(endpoint);
  } catch {
    return false;
  }
  const host = url.hostname.toLowerCase();
  if (host === 'localhost' || host === '127.0.0.1' || host === '::1') return true;
  // Match against the browser's current hostname — same host, regardless
  // of port. This catches `http://my-aao.example/v1` when the operator
  // is editing settings on `my-aao.example` itself.
  if (typeof window !== 'undefined' && window.location?.hostname) {
    return host === window.location.hostname.toLowerCase();
  }
  return false;
}

/**
 * Settings → LLM → Workers.
 *
 * This is the v2 replacement for the old `ProviderForm` + inline
 * `WorkersBlock` pair. The big differences from the v1 forms:
 *
 *   - reads/writes `llm.workers[]` instead of `provider.workers[]`
 *     (the v1 form rendered empty after the API switched to v2 shape)
 *   - each row carries `connectionType: direct | aao_gateway` instead
 *     of a `proxy: true` toggle, so the rendered help text and warnings
 *     can be specific to the connection style
 *   - api keys use the 4-state `SecretInput` editor instead of a raw
 *     `<input type="password">`, so masking / env-refs / clears are
 *     explicit and survive round-trip without a magic `'********'`
 *     literal sneaking back into config.yaml
 *   - the model field is a discovery-backed dropdown with manual
 *     fallback — typing a literal still works, but Ollama-style
 *     `/models` endpoints pre-populate the dropdown
 *   - roles use a chip editor instead of a comma-separated string, so
 *     values containing commas are no longer corrupted
 *   - `aao_gateway` rows show a heuristic self-loop warning when the
 *     endpoint host looks like the current AAO instance
 */
export function LlmWorkersForm({ config, onChange, overriddenByEnv }: SectionFormProps) {
  const { t } = useTranslation('settings');
  const llm: LlmConfigShape = config.llm ?? {};
  const workers: LlmWorker[] = Array.isArray(llm.workers) ? llm.workers : [];
  const retry = llm.retry ?? {};

  const updateWorker = (index: number, patch: Partial<LlmWorker>) => {
    const next = workers.map((w, i) => (i === index ? { ...w, ...patch } : w));
    onChange('llm.workers', next);
  };

  const removeWorker = (index: number) => {
    onChange('llm.workers', workers.filter((_, i) => i !== index));
  };

  const moveWorker = (index: number, delta: number) => {
    const target = index + delta;
    if (target < 0 || target >= workers.length) return;
    const next = [...workers];
    const [removed] = next.splice(index, 1);
    next.splice(target, 0, removed);
    onChange('llm.workers', next);
  };

  const addWorker = () => {
    const next: LlmWorker = {
      id: `worker-${workers.length + 1}`,
      connectionType: 'direct',
      endpoint: '',
      enabled: true,
      maxConcurrency: 1,
      roles: [],
    };
    onChange('llm.workers', [...workers, next]);
  };

  // Pre-compute self-loop verdicts once per render so we don't recompute
  // URL parsing inside the row JSX. Endpoint-only dependency is enough:
  // connection_type is checked at render site.
  const selfLoopFlags = useMemo(
    () => workers.map(w => detectSelfLoop(w.endpoint)),
    [workers],
  );

  return (
    <div className="space-y-5">
      <div>
        <h2 className="text-base font-semibold text-slate-800 mb-1">{t('llmWorkers.title')}</h2>
        <p className="text-xs text-slate-500 leading-relaxed">
          {t('llmWorkers.intro')}<br />
          {t('llmWorkers.rolesHelp')}
        </p>
      </div>

      <div className="space-y-3">
        {workers.length === 0 && (
          <div className="text-xs text-slate-500 border border-dashed border-slate-200 rounded p-4 text-center">
            {t('llmWorkers.empty')}
          </div>
        )}

        {workers.map((w, i) => {
          const isGateway = w.connectionType === 'aao_gateway' || w.proxy === true;
          const showSelfLoop = isGateway && selfLoopFlags[i];
          const endpointOverridden = i === 0 && overriddenByEnv['llm.workers[0].endpoint'];
          const modelOverridden = i === 0 && overriddenByEnv['llm.workers[0].model'];
          return (
            <div key={i} className="border border-slate-200 rounded-lg p-4 space-y-3 relative">
              <div className="absolute top-2 right-2 flex gap-1">
                <button
                  onClick={() => moveWorker(i, -1)}
                  disabled={i === 0}
                  title={t('llmWorkers.moveUp')}
                  className="text-slate-400 hover:text-slate-700 text-sm leading-none disabled:opacity-30 disabled:cursor-not-allowed px-1"
                >
                  ↑
                </button>
                <button
                  onClick={() => moveWorker(i, 1)}
                  disabled={i === workers.length - 1}
                  title={t('llmWorkers.moveDown')}
                  className="text-slate-400 hover:text-slate-700 text-sm leading-none disabled:opacity-30 disabled:cursor-not-allowed px-1"
                >
                  ↓
                </button>
                <button
                  onClick={() => removeWorker(i)}
                  title={t('llmWorkers.removeWorker')}
                  className="text-slate-400 hover:text-red-500 text-lg leading-none px-1"
                >
                  &times;
                </button>
              </div>

              <div className="grid grid-cols-2 gap-3">
                <div>
                  <FieldLabel>ID</FieldLabel>
                  <FieldInput value={w.id ?? ''} onChange={v => updateWorker(i, { id: v })} />
                </div>

                <div>
                  <FieldLabel>{t('llmWorkers.connectionType')}</FieldLabel>
                  <select
                    value={w.connectionType ?? (w.proxy === true ? 'aao_gateway' : 'direct')}
                    onChange={e => {
                      const next = e.target.value as 'direct' | 'aao_gateway';
                      // Keep the legacy `proxy` flag in sync so an
                      // operator who downgrades to a v1 build doesn't
                      // lose the routing semantics.
                      updateWorker(i, {
                        connectionType: next,
                        proxy: next === 'aao_gateway' ? true : undefined,
                      });
                    }}
                    className="w-full h-8 px-2 text-[13px] border border-hairline rounded-md focus:ring-2 focus:ring-accent-ring focus:border-accent outline-none bg-canvas"
                  >
                    <option value="direct">Direct (Ollama / vLLM / llama.cpp)</option>
                    <option value="aao_gateway">AAO Gateway</option>
                  </select>
                </div>

                <div className="col-span-2">
                  <FieldLabel>Endpoint</FieldLabel>
                  <FieldInput
                    value={w.endpoint ?? ''}
                    onChange={v => updateWorker(i, { endpoint: v })}
                    disabled={!!endpointOverridden}
                    disabledReason={t('llmWorkers.endpointOverride')}
                    placeholder={
                      isGateway
                        ? 'http://gateway.example.com:9876/v1'
                        : 'http://localhost:11434/v1'
                    }
                  />
                  {endpointOverridden && <EnvOverrideWarning />}
                  {showSelfLoop && (
                    <p className="text-2xs text-amber-700 dark:text-amber-300 bg-amber-50 dark:bg-amber-500/15 border border-amber-200 dark:border-amber-500/30 rounded px-2 py-1 mt-1">
                      {t('llmWorkers.selfLoopWarn')}
                    </p>
                  )}
                </div>

                <div className="col-span-2">
                  <FieldLabel>{isGateway ? t('llmWorkers.apiKeyRequired') : t('llmWorkers.apiKeyOptional')}</FieldLabel>
                  <SecretInput
                    rawValue={w.apiKey ?? ''}
                    onChange={v => updateWorker(i, { apiKey: v === '' ? '' : v })}
                    placeholder={isGateway ? 'sk-aao-...' : t('llmWorkers.apiKeyOptionalPlaceholder')}
                  />
                  <HelpText>
                    {isGateway ? t('llmWorkers.apiKeyGatewayHelp') : t('llmWorkers.apiKeyDirectHelp')}
                  </HelpText>
                </div>

                <div className="col-span-2">
                  <FieldLabel>Model</FieldLabel>
                  <ModelSelect
                    value={w.model ?? ''}
                    onChange={v => updateWorker(i, { model: v || undefined })}
                    endpoint={w.endpoint}
                    apiKeyRaw={w.apiKey}
                  />
                  {modelOverridden && <EnvOverrideWarning />}
                  <HelpText>
                    {t('llmWorkers.modelHelp')}
                  </HelpText>
                </div>

                <div className="col-span-2">
                  <FieldLabel>Roles</FieldLabel>
                  <StringArrayEditor
                    value={Array.isArray(w.roles) ? w.roles : []}
                    onChange={roles => updateWorker(i, { roles })}
                    placeholder="auto / fast / quality / reflection / title"
                  />
                </div>

                <div>
                  <FieldLabel>{t('llmWorkers.maxConcurrency')}</FieldLabel>
                  <FieldInput
                    type="number"
                    value={w.maxConcurrency ?? 1}
                    onChange={v => updateWorker(i, { maxConcurrency: Number(v) })}
                  />
                </div>

                <div className="flex items-center gap-5 pt-5 flex-wrap">
                  <label className="flex items-center gap-2 text-sm text-slate-600 cursor-pointer">
                    <input
                      type="checkbox"
                      checked={w.enabled !== false}
                      onChange={e => updateWorker(i, { enabled: e.target.checked })}
                      className="rounded"
                    />
                    {t('llmWorkers.enabled')}
                  </label>
                  <label
                    className="flex items-center gap-2 text-sm text-slate-600 cursor-pointer"
                    title={t('llmWorkers.vlmTitle')}
                  >
                    <input
                      type="checkbox"
                      checked={w.vlm === true}
                      onChange={e => updateWorker(i, { vlm: e.target.checked || undefined })}
                      className="rounded"
                    />
                    VLM
                  </label>
                </div>
              </div>
            </div>
          );
        })}

        <button
          onClick={addWorker}
          className="px-4 py-2 text-sm text-accent border border-accent rounded-lg hover:bg-accent-soft"
        >
          {t('llmWorkers.addWorker')}
        </button>
      </div>

      <h3 className="text-sm font-medium text-slate-600 mt-4 pt-3 border-t border-slate-200">
        {t('llmWorkers.globalTitle')}
      </h3>

      <div>
        <FieldLabel>Timeout (minutes)</FieldLabel>
        <FieldInput
          type="number"
          value={llm.timeoutMinutes ?? 10}
          onChange={v => onChange('llm.timeoutMinutes', Number(v))}
        />
        <HelpText>{t('llmWorkers.timeoutHelp')}</HelpText>
      </div>

      <h3 className="text-sm font-medium text-slate-600 mt-4 pt-3 border-t border-slate-200">
        {t('llmWorkers.retryTitle')}
      </h3>

      <div>
        <FieldLabel>Max Attempts</FieldLabel>
        <FieldInput
          type="number"
          value={retry.maxAttempts ?? 3}
          onChange={v => onChange('llm.retry.maxAttempts', Number(v))}
        />
        <HelpText>{t('llmWorkers.maxAttemptsHelp')}</HelpText>
      </div>

      <div>
        <FieldLabel>Backoff (ms)</FieldLabel>
        <StringArrayEditor
          value={Array.isArray(retry.backoffMs) ? retry.backoffMs.map(n => String(n)) : []}
          onChange={vs => {
            const nums = vs.map(s => Number(s.trim())).filter(n => !isNaN(n));
            onChange('llm.retry.backoffMs', nums);
          }}
          placeholder="2000"
        />
        <HelpText>{t('llmWorkers.backoffHelp')}</HelpText>
      </div>

      <div>
        <FieldLabel>Retryable Status Codes</FieldLabel>
        <StringArrayEditor
          value={Array.isArray(retry.retryableStatus) ? retry.retryableStatus.map(n => String(n)) : []}
          onChange={vs => {
            const nums = vs.map(s => Number(s.trim())).filter(n => !isNaN(n));
            onChange('llm.retry.retryableStatus', nums);
          }}
          placeholder="429"
        />
        <HelpText>{t('llmWorkers.retryableStatusHelp')}</HelpText>
      </div>
    </div>
  );
}
