const API_BASE_URL = '/api/v1';

const TOKEN_KEY = 'auth_token';

/** Read the stored JWT without importing AuthContext (avoids circular deps). */
function getStoredToken(): string | null {
  return localStorage.getItem(TOKEN_KEY);
}

interface ApiErrorPayload {
  detail?: string;
  message?: string;
  error?: string;
}

function buildUrl(endpoint: string): string {
  return `${API_BASE_URL}${endpoint.startsWith('/') ? endpoint : `/${endpoint}`}`;
}

async function readErrorMessage(response: Response): Promise<string> {
  try {
    const payload = (await response.json()) as ApiErrorPayload;
    return payload.detail || payload.message || payload.error || `${response.status} ${response.statusText}`;
  } catch {
    return `${response.status} ${response.statusText}`;
  }
}

/** Inject Authorization header when a token is available. */
function withAuth(headers: Headers): Headers {
  const token = getStoredToken();
  if (token && !headers.has('Authorization')) {
    headers.set('Authorization', `Bearer ${token}`);
  }
  return headers;
}

/** Handle 401 by clearing the stored token so the app redirects to login. */
function handle401() {
  localStorage.removeItem(TOKEN_KEY);
  // Emit a custom event so AuthContext / router can react without a direct import.
  window.dispatchEvent(new CustomEvent('auth:unauthorized'));
}

export async function fetchAPI<T>(endpoint: string, options?: RequestInit): Promise<T> {
  const headers = withAuth(new Headers(options?.headers));
  if (!headers.has('Accept')) {
    headers.set('Accept', 'application/json');
  }
  if (!headers.has('Content-Type') && !(options?.body instanceof FormData)) {
    headers.set('Content-Type', 'application/json');
  }

  const response = await fetch(buildUrl(endpoint), {
    ...options,
    headers,
  });

  if (response.status === 401) {
    handle401();
    throw new Error('Session expired, please log in again.');
  }

  if (!response.ok) {
    throw new Error(`API Error: ${await readErrorMessage(response)}`);
  }

  return response.json() as Promise<T>;
}

export interface SSEMessage {
  type: string;
  text?: string;
  docs?: RetrievedDoc[];
  session_id?: string;
  // ── P0-1 Agentic-mode thinking-step fields ────────────────────────────────
  // Populated when type === 'thinking'; maps to the backend IntentResult /
  // GroundingResult / retrieval step payloads emitted by AgenticConversationService.
  step?: string;           // intent_analysis | query_planning | retrieving | grounding_check
  status?: string;         // running | done
  intent_type?: string;    // simple_qa | compare | multi_hop | ambiguous
  requires_decomposition?: boolean;
  reason?: string;
  sub_queries?: string[];
  query?: string;          // sub-query being retrieved
  index?: number;          // 1-based sub-query index
  total?: number;          // total sub-query count
  found?: number;          // chunks found for this sub-query
  retry?: boolean;         // true when this is a grounding-failure re-query
  sufficient?: boolean;    // grounding check result
  confidence?: number;     // grounding confidence 0–1
}

export async function streamSSE<TMessage extends SSEMessage>(
  endpoint: string,
  body: unknown,
  onMessage: (data: TMessage) => void,
  onError?: (error: Error) => void,
  onComplete?: () => void
): Promise<void> {
  const headers: Record<string, string> = {
    Accept: 'text/event-stream',
    'Content-Type': 'application/json',
  };
  const token = getStoredToken();
  if (token) headers['Authorization'] = `Bearer ${token}`;

  const response = await fetch(buildUrl(endpoint), {
    method: 'POST',
    headers,
    body: JSON.stringify(body),
  });

  if (response.status === 401) {
    handle401();
    onError?.(new Error('Session expired, please log in again.'));
    return;
  }

  if (!response.ok) {
    onError?.(new Error(`HTTP error! status: ${await readErrorMessage(response)}`));
    return;
  }

  const reader = response.body?.getReader();
  if (!reader) {
    onError?.(new Error('No response body'));
    return;
  }

  const decoder = new TextDecoder();
  let buffer = '';

  try {
    while (true) {
      const { done, value } = await reader.read();
      if (done) {
        break;
      }

      buffer += decoder.decode(value, { stream: true });
      const events = buffer.split('\n\n');
      buffer = events.pop() || '';

      for (const eventBlock of events) {
        const dataLines = eventBlock
          .split('\n')
          .filter((line) => line.startsWith('data:'))
          .map((line) => line.slice(5).trim());

        if (dataLines.length === 0) {
          continue;
        }

        const rawData = dataLines.join('\n');
        try {
          onMessage(JSON.parse(rawData) as TMessage);
        } catch {
          onMessage({ type: 'raw', text: rawData } as TMessage);
        }
      }
    }

    if (buffer.trim()) {
      const dataLines = buffer
        .split('\n')
        .filter((line) => line.startsWith('data:'))
        .map((line) => line.slice(5).trim());

      if (dataLines.length > 0) {
        const rawData = dataLines.join('\n');
        try {
          onMessage(JSON.parse(rawData) as TMessage);
        } catch {
          onMessage({ type: 'raw', text: rawData } as TMessage);
        }
      }
    }

    onComplete?.();
  } catch (error) {
    onError?.(error instanceof Error ? error : new Error(String(error)));
  }
}

export interface DocInfo {
  id: string;
  name: string;
  chunks: number;
  status: string;
  updated_at?: string;
  download_url?: string;
  size_text?: string;
  summary?: string;
  regulation_type?: string;
  version?: string;
}

export interface DocListResponse {
  docs: DocInfo[];
}

export interface DocUploadResponse {
  doc_id: string;
  doc_name: string;
  status: string;
  message?: string;
  num_chunks?: number;
  summary?: string;
  regulation_type?: string;
  version?: string;
}

export interface QuickQuestion {
  id: string;
  question: string;
  category: string;
}

export interface QuickQuestionsResponse {
  questions: QuickQuestion[];
}

export interface RetrievedDoc {
  id: string;
  score: number;
  preview: string;
  doc_name: string;
  clause: string;
  doc_id?: string;
  download_url?: string;
}

export interface Regulation {
  id: number;
  name: string;
  clause: string | null;
  score: number;
  match_keyword: string;
  category: string;
  full_content: string;
}

export interface ComplianceSegment {
  id: number;
  index: number;
  intent: string;
  start_pos: number;
  end_pos: number;
  content: string;
  risk_level: string;
  regulations: Regulation[];
}

export interface RiskDashboard {
  score: number;
  high_risk_count: number;
  medium_risk_count: number;
  low_risk_count: number;
  need_fix_segments: number;
  status: string;
  status_label: string;
}

export interface PriorityAction {
  regulation: string;
  issue: string;
  suggestion: string;
  severity: string;
}

export interface ComplianceResult {
  task_id: string;
  dashboard: RiskDashboard;
  segments: ComplianceSegment[];
  priority_actions: PriorityAction[];
}

export interface SystemStats {
  documents_total: number;
  documents_indexed: number;
  documents_failed: number;
  chunks_total: number;
}

export interface SystemConfig {
  embedding_model: string;
  embedding_dim: number;
  embedding_base_url: string;
  milvus_collection: string;
  llm_provider: string;
  llm_model: string;
  document_metadata_path: string;
}

export interface ServiceHealth {
  status: 'ok' | 'error' | 'unknown';
  error?: string;
}

export interface SystemHealth {
  milvus: ServiceHealth & {
    connected?: boolean;
    collection_name?: string;
    num_entities?: number;
  };
  minio: ServiceHealth & { connected?: boolean };
  bm25: { available: boolean };
  reranker: { enabled: boolean; model?: string | null };
  sessions: { active: number; max: number };
}

export type ModelRole = 'main_llm' | 'hyde_llm' | 'embedding' | 'reranker';
export type ModelStatus = 'ok' | 'error' | 'never_called' | 'disabled';

export interface ModelUsageEntry {
  role: ModelRole;
  role_label: string;
  provider: string;
  model: string;
  enabled: boolean;
  status: ModelStatus;
  total_tokens: number;
  call_count_ok: number;
  call_count_error: number;
  last_called_at: string | null;
  last_latency_ms: number | null;
  last_error: string | null;
  shares_usage_with: ModelRole | null;
}

export interface ModelUsageResponse {
  models: ModelUsageEntry[];
}

export { API_BASE_URL };
