/**
 * Dual-mode API client for Portal
 * - In Tauri app: uses `invoke` to call Rust commands
 * - In browser dev mode: uses direct HTTP fetch to backend API
 */

import { ref } from 'vue'

// ── Global API Debug State ──────────────────────────────────────────────
export const lastApiCall = ref<any>(null)

// ── Types ───────────────────────────────────────────────────────────────

export interface PortalConfig {
  api_base_url: string
  api_key: string
  timeout_secs: number
}

export interface SearchRequest {
  query: string
  limit?: number
  mode?: string
  uuid?: string
  filters?: Record<string, unknown>
}

export interface SearchResult {
  query: string
  count: number
  hits: SearchHit[]
}

export interface SearchHit {
  id: string
  vid: string
  start_frame: number
  end_frame: number
  fps: number
  start: number
  end: number
  text: string
  score: number
  title?: string
  file_path?: string
  has_visual_stats?: boolean
  parent_id?: string
}

export interface RegisterResponse {
  success: boolean
  file_uuid: string
  file_name: string
  file_path: string
  duration: number
  width: number
  height: number
  fps: number
  total_frames: number
  registration_time: string | null
  already_exists: boolean
  message: string
}

export interface UnregisterResponse {
  success: boolean
  message: string
  file_uuid: string
  deleted_face_detections: number
  deleted_processor_results: number
  deleted_chunks: number
}

// ── Config (browser-only, stored in localStorage) ───────────────────────

const DEFAULT_CONFIG: PortalConfig = {
  api_base_url: import.meta.env.VITE_API_BASE_URL || 'http://127.0.0.1:3003',
  api_key: import.meta.env.VITE_API_KEY || '',
  timeout_secs: 30,
}

function getConfig(): PortalConfig {
  const stored = localStorage.getItem('portal_config')
  if (stored) {
    try {
      return JSON.parse(stored)
    } catch {
      return DEFAULT_CONFIG
    }
  }
  return DEFAULT_CONFIG
}

export function saveConfig(config: PortalConfig): void {
  localStorage.setItem('portal_config', JSON.stringify(config))
}

export async function logout(): Promise<void> {
  try {
    const config = getConfig();
    const apiKey = config.api_key || localStorage.getItem('momentry_api_key');
    if (apiKey) {
      // Call logout API to invalidate session on server side (if implemented)
      // For now, just best effort
      await fetch(`${config.api_base_url}/api/v1/auth/logout`, {
        method: 'POST',
        headers: { 'X-API-Key': apiKey }
      }).catch(() => {}); // Ignore network errors
    }
  } catch (e) {
    console.warn('Logout API call failed:', e);
  } finally {
    handleSessionExpired();
  }
}

// ── Environment detection ───────────────────────────────────────────────

function isTauri(): boolean {
  return '__TAURI_INTERNALS__' in window || '__TAURI__' in window
}

// ── HTTP fetch wrapper (browser) ────────────────────────────────────────

// Helper to handle session expiration
function handleSessionExpired() {
  console.warn("Session expired or connection error, redirecting to login...");
  localStorage.removeItem('momentry_user');
  localStorage.removeItem('portal_config');
  localStorage.removeItem('momentry_api_key');
  if (window.location.pathname !== '/login') {
    window.location.href = '/login';
  }
}

// Retry fetch logic
export async function httpFetch<T>(url: string, options?: RequestInit, retries = 3): Promise<T> {
  // Re-read config to ensure we have the latest key if it changed
  const config = getConfig();
  
  // Fallback key check
  const apiKey = config.api_key || localStorage.getItem('momentry_api_key') || '';

  const headers = new Headers(options?.headers);
  headers.set('Content-Type', 'application/json');
  if (apiKey) {
    headers.set('X-API-Key', apiKey);
  }

  const method = options?.method || 'GET'

  // Update debug state (only on first attempt)
  if (retries === 3) {
    lastApiCall.value = {
      type: 'HTTP',
      method,
      url,
      headers: { ...headers, 'X-API-Key': apiKey ? apiKey.substring(0, 10) + '...' : 'none' }, 
      body: options?.body ? JSON.parse(options.body as string) : null,
      status: 'loading',
      data: null,
      timestamp: new Date().toISOString()
    }
  }

  const controller = new AbortController()
  const timeout = setTimeout(() => controller.abort(), config.timeout_secs * 1000)

  try {
    const resp = await fetch(url, {
      ...options,
      headers,
      signal: controller.signal,
    })

    // Handle 401 Unauthorized immediately
    if (resp.status === 401) {
      handleSessionExpired();
      throw new Error("Unauthorized");
    }

    if (!resp.ok) {
      const text = await resp.text()
      lastApiCall.value = { ...lastApiCall.value, status: `Error ${resp.status}`, data: text }
      // Don't redirect on 500/404, just throw
      throw new Error(`HTTP ${resp.status}: ${text}`)
    }

    const contentType = resp.headers.get('content-type')
    let data: any;
    if (contentType?.includes('application/json')) {
      data = await resp.json()
    } else {
      data = await resp.text()
    }
    
    lastApiCall.value = { ...lastApiCall.value, status: `OK ${resp.status}`, data }
    return data as Promise<T>

  } catch (e: any) {
    // Network error (server restart/timeout)
    // e.name === 'TypeError' usually indicates network error in fetch
    if (e.name === 'TypeError' && retries > 0) {
      console.warn(`Network error, retrying... (${retries} attempts left)`);
      await new Promise(r => setTimeout(r, 1000)); // Wait 1s before retry
      return httpFetch(url, options, retries - 1);
    }
    
    // If retries exhausted or it's a different error
    lastApiCall.value = { ...lastApiCall.value, status: 'Error', data: e?.message || e }
    
    // If network error and no retries left, redirect to login
    if (e.name === 'TypeError') {
      handleSessionExpired();
    }
    
    throw e
  } finally {
    clearTimeout(timeout)
  }
}

async function tauriInvoke<T>(command: string, args?: Record<string, unknown>): Promise<T> {
  const { invoke } = await import('@tauri-apps/api/core')
  
  lastApiCall.value = {
    type: 'TAURI',
    command,
    args: args || {},
    status: 'loading',
    data: null,
    timestamp: new Date().toISOString()
  }

  try {
    const result = await invoke<T>(command, args)
    lastApiCall.value = { ...lastApiCall.value, status: 'Success', data: result }
    return result
  } catch (e) {
    lastApiCall.value = { ...lastApiCall.value, status: 'Error', data: e }
    throw e
  }
}

// ── Unified API functions ───────────────────────────────────────────────

export async function searchVideos(query: string, limit = 10, mode = 'vector'): Promise<SearchResult> {
  if (isTauri()) {
    return tauriInvoke<SearchResult>('search_videos', { query, limit, mode })
  }

  const config = getConfig()
  const url = mode === 'smart' || mode === 'bm25'
    ? `${config.api_base_url}/api/v1/search`
    : `${config.api_base_url}/api/v1/search`

  const response: any = await httpFetch<any>(url, {
    method: 'POST',
    body: JSON.stringify({ query, limit }),
  })

  // Map backend response ({ results: [...], query: string }) to frontend SearchResult ({ hits: [...], query: string, count: number })
  return {
    query: response.query,
    count: response.results?.length || 0,
    hits: (response.results || []).map((r: any) => ({
      id: r.chunk_id || r.id,
      vid: r.uuid || r.vid,
      start_frame: Math.floor((r.start_time || 0) * 30),
      end_frame: Math.floor((r.end_time || 0) * 30),
      fps: 30,
      start: r.start_time || r.start || 0,
      end: r.end_time || r.end || 0,
      text: r.text || '',
      score: r.score || 0,
      title: r.title || r.file_name,
      file_path: r.file_path,
      has_visual_stats: !!r.visual_stats,
      parent_id: r.parent_chunk_id,
    })),
  }
}

export async function searchChunks(query: string, uuid?: string): Promise<SearchResult> {
  if (isTauri()) {
    return tauriInvoke<SearchResult>('search_chunks', { query, uuid })
  }

  const config = getConfig()
  const url = uuid
    ? `${config.api_base_url}/api/v1/search?uuid=${encodeURIComponent(uuid)}`
    : `${config.api_base_url}/api/v1/search`

  const response: any = await httpFetch<any>(url, {
    method: 'POST',
    body: JSON.stringify({ query, limit: 10 }),
  })

  return {
    query: response.query,
    count: response.results?.length || 0,
    hits: (response.results || []).map((r: any) => ({
      id: r.chunk_id || r.id,
      vid: r.uuid || r.vid,
      start_frame: Math.floor((r.start_time || 0) * 30),
      end_frame: Math.floor((r.end_time || 0) * 30),
      fps: 30,
      start: r.start_time || r.start || 0,
      end: r.end_time || r.end || 0,
      text: r.text || '',
      score: r.score || 0,
      title: r.title || r.file_name,
      file_path: r.file_path,
      has_visual_stats: !!r.visual_stats,
      parent_id: r.parent_chunk_id,
    })),
  }
}

export async function getHealth(): Promise<any> {
  if (isTauri()) {
    return tauriInvoke('get_health_detailed')
  }

  const config = getConfig()
  return httpFetch(`${config.api_base_url}/health/detailed`)
}

export async function getIngestStats(): Promise<any> {
  if (isTauri()) {
    return tauriInvoke('get_ingest_stats')
  }

  const config = getConfig()
  return httpFetch(`${config.api_base_url}/api/v1/stats/ingest`)
}

export async function getSftpgoStatus(): Promise<any> {
  if (isTauri()) {
    return tauriInvoke('get_sftpgo_status')
  }

  const config = getConfig()
  return httpFetch(`${config.api_base_url}/api/v1/stats/sftpgo`)
}

export async function getInferenceHealth(): Promise<any> {
  if (isTauri()) {
    return tauriInvoke('get_inference_health')
  }

  const config = getConfig()
  return httpFetch(`${config.api_base_url}/api/v1/stats/inference`)
}

export async function getVideos(
  query?: string,
  status?: string,
  page: number = 1,
  page_size: number = 10,
  uuid?: string
): Promise<any> {
  if (isTauri()) {
    return tauriInvoke('get_videos', { query, status, page, page_size, uuid })
  }

  const config = getConfig()
  const params = new URLSearchParams()
  if (query) params.append('q', query)
  if (status) params.append('status', status)
  if (uuid) params.append('uuid', uuid)
  params.append('page', String(page))
  params.append('page_size', String(page_size))

  return httpFetch(`${config.api_base_url}/api/v1/videos?${params.toString()}`)
}

export async function listIdentities(uuid?: string): Promise<any> {
  if (isTauri()) {
    return tauriInvoke('list_identities', { uuid })
  }

  const config = getConfig()
  const url = uuid
    ? `${config.api_base_url}/api/v1/identities?uuid=${encodeURIComponent(uuid)}`
    : `${config.api_base_url}/api/v1/identities`

  return httpFetch(url)
}

export async function translateText(text: string, targetLang: string = 'zh-TW'): Promise<string> {
  if (isTauri()) {
    return tauriInvoke<string>('translate_text', { text, target_lang: targetLang })
  }

  try {
    // Use our internal Agent API
    const config = getConfig()
    const response = await httpFetch<any>(`${config.api_base_url}/api/v1/agents/translate`, {
      method: 'POST',
      body: JSON.stringify({ 
        text, 
        target_language: targetLang 
      })
    })
    
    if (response.success && response.translated_text) {
      return response.translated_text
    }
  } catch (e) {
    console.warn('Translation Agent failed:', e)
  }
  
  return text
}

export async function getPersonThumbnail(personId: string): Promise<string> {
  if (isTauri()) {
    return tauriInvoke<string>('get_person_thumbnail_b64', { person_id: personId })
  }

  const config = getConfig()
  return `${config.api_base_url}/api/v1/people/${personId}/thumbnail`
}

export async function registerIdentity(name: string, images: string[]): Promise<any> {
  if (isTauri()) {
    return tauriInvoke('register_identity', { name, images })
  }

  const config = getConfig()
  return httpFetch(`${config.api_base_url}/api/v1/identities/from-person`, {
    method: 'POST',
    body: JSON.stringify({ name, images }),
  })
}

export async function processVideo(uuid: string, processors?: string[]): Promise<any> {
  if (isTauri()) {
    return tauriInvoke('process_video', { uuid, processors })
  }

  const config = getConfig()
  const body = processors ? { processors } : {}
  return httpFetch(`${config.api_base_url}/api/v1/assets/${uuid}/process`, {
    method: 'POST',
    body: JSON.stringify(body),
  })
}

export async function registerVideo(filePath: string): Promise<RegisterResponse> {
  if (isTauri()) {
    return tauriInvoke<RegisterResponse>('register_video', { file_path: filePath })
  }

  const config = getConfig()
  return httpFetch<RegisterResponse>(`${config.api_base_url}/api/v1/files/register`, {
    method: 'POST',
    body: JSON.stringify({ file_path: filePath }),
  })
}

export async function unregisterVideo(fileUuid: string): Promise<UnregisterResponse> {
  if (isTauri()) {
    return tauriInvoke<UnregisterResponse>('unregister_video', { file_uuid: fileUuid })
  }

  const config = getConfig()
  return httpFetch<UnregisterResponse>(`${config.api_base_url}/api/v1/videos/${fileUuid}`, {
    method: 'DELETE',
  })
}

export async function listFaceCandidates(fileUuid?: string, minConfidence = 0.5, page = 1, pageSize = 20): Promise<any> {
  if (isTauri()) {
    return tauriInvoke('list_face_candidates', { file_uuid: fileUuid, min_confidence: minConfidence, page, page_size: pageSize })
  }

  const config = getConfig()
  const params = new URLSearchParams()
  if (fileUuid) params.append('file_uuid', fileUuid)
  params.append('min_confidence', String(minConfidence))
  params.append('page', String(page))
  params.append('page_size', String(pageSize))

  return httpFetch(`${config.api_base_url}/api/v1/faces/candidates?${params.toString()}`)
}

export async function getIdentityFaces(identityId: number, page = 1, pageSize = 100): Promise<any> {
  if (isTauri()) {
    return tauriInvoke('get_identity_faces', { identity_id: identityId, page, page_size: pageSize })
  }

  const config = getConfig()
  const params = new URLSearchParams()
  params.append('page', String(page))
  params.append('page_size', String(pageSize))

  return httpFetch(`${config.api_base_url}/api/v1/identities/${identityId}/faces?${params.toString()}`)
}

// ── Config helpers ──────────────────────────────────────────────────────

export function getCurrentConfig(): PortalConfig {
  if (isTauri()) {
    return getConfig() // Will be overridden by Tauri config if needed
  }
  return getConfig()
}

export { isTauri }
