/**
 * Verified provider connection state for /api/meta badge + createJob gate.
 * A model string is only shown after a successful activation verify (or
 * bootstrap-from-env verify at startup). Never invent a default model label.
 */
import { resolveActiveModel } from '../core/lmsModels';

export type ConnectionKind = 'mock' | 'local' | 'cloud';

export interface ConnectionState {
  connected: boolean;
  type: 'claude' | 'openai-compat' | 'mock' | null;
  kind: ConnectionKind | null;
  /** Display label (model id, or "id @ url" for local). Null when disconnected. */
  model: string | null;
  verifiedAt: string | null;
  role?: 'operator' | 'supervisor' | 'pool';
}

let connection: ConnectionState = {
  connected: false,
  type: null,
  kind: null,
  model: null,
  verifiedAt: null,
};

export function getConnection(): ConnectionState {
  return { ...connection };
}

export function clearConnection(): void {
  connection = {
    connected: false,
    type: null,
    kind: null,
    model: null,
    verifiedAt: null,
  };
}

export function setConnection(next: Omit<ConnectionState, 'connected'> & { connected?: boolean }): void {
  connection = {
    connected: next.connected ?? true,
    type: next.type,
    kind: next.kind,
    model: next.model,
    verifiedAt: next.verifiedAt,
    role: next.role,
  };
}

export function isProviderConnected(): boolean {
  return connection.connected && !!connection.model;
}

/** Verify openai-compat / local: GET /models must return at least one id. */
export async function verifyOpenAiCompat(
  baseURL: string,
  apiKey?: string,
): Promise<{ ok: true; model: string; modelId?: string } | { ok: false; error: string }> {
  const base = baseURL.replace(/\/$/, '') || 'http://localhost:1234/v1';
  try {
    const headers: Record<string, string> = {};
    if (apiKey) headers.Authorization = `Bearer ${apiKey}`;
    const r = await fetch(`${base}/models`, { headers });
    if (!r.ok) return { ok: false, error: `GET ${base}/models → HTTP ${r.status}` };
    const j = await r.json() as { data?: { id: string }[] };
    if (!j.data?.length) return { ok: false, error: `GET ${base}/models returned no models` };
    // Label with the LOADED model when the endpoint reports load state; the
    // model itself is resolved fresh per job (follow-the-loaded-model) — this
    // is display only, never a pin.
    const active = await resolveActiveModel(base);
    const shown = active ? active.id : '(follows loaded model)';
    return { ok: true, model: `${shown} @ ${base}`, modelId: active?.id };
  } catch (e) {
    return { ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}

/** Lightweight Claude auth check via GET /v1/models. */
export async function verifyClaude(
  apiKey: string,
): Promise<{ ok: true; model: string } | { ok: false; error: string }> {
  if (!apiKey?.trim()) return { ok: false, error: 'Claude API key required' };
  try {
    const r = await fetch('https://api.anthropic.com/v1/models', {
      headers: {
        'x-api-key': apiKey,
        'anthropic-version': '2023-06-01',
      },
    });
    if (!r.ok) {
      const body = await r.text().catch(() => '');
      return { ok: false, error: `Anthropic /v1/models → HTTP ${r.status}${body ? `: ${body.slice(0, 200)}` : ''}` };
    }
    return { ok: true, model: '' }; // caller supplies activated model id
  } catch (e) {
    return { ok: false, error: e instanceof Error ? e.message : String(e) };
  }
}
