import type { APIClientConfig } from '../../types/core/api_client_config'

/** Metodos que retorna el factory api_client. */
export interface APIClient {
  get: <T>(path: string, params?: Record<string, string>) => Promise<T>
  post: <T>(path: string, body?: unknown) => Promise<T>
  put: <T>(path: string, body?: unknown) => Promise<T>
  patch: <T>(path: string, body?: unknown) => Promise<T>
  del: <T>(path: string) => Promise<T>
}

/**
 * Factory que crea un cliente HTTP configurado con base_url y headers fijos.
 * Parsea JSON automaticamente. Llama a on_error/on_unauthorized segun el status.
 */
export function apiClient(config: APIClientConfig): APIClient {
  const { base_url, headers: baseHeaders = {}, on_error, on_unauthorized } = config

  const buildUrl = (path: string, params?: Record<string, string>): string => {
    const url = `${base_url}${path}`
    if (!params || Object.keys(params).length === 0) return url
    const qs = new URLSearchParams(params).toString()
    return `${url}?${qs}`
  }

  const buildHeaders = (extra: Record<string, string> = {}): HeadersInit => ({
    'Accept': 'application/json',
    ...baseHeaders,
    ...extra,
  })

  const handleResponse = async <T>(res: Response, url: string, init?: RequestInit): Promise<T> => {
    if (res.status === 401) {
      on_unauthorized?.()
      const err = new Error(`Unauthorized: ${url}`)
      on_error?.(err, url, init)
      throw err
    }

    if (!res.ok) {
      const text = await res.text().catch(() => '')
      const err = new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`)
      on_error?.(err, url, init)
      throw err
    }

    const contentType = res.headers.get('content-type') ?? ''
    if (contentType.includes('application/json')) {
      return res.json() as Promise<T>
    }
    return res.text() as unknown as T
  }

  const request = async <T>(
    method: string,
    path: string,
    body?: unknown,
    params?: Record<string, string>,
  ): Promise<T> => {
    const url = buildUrl(path, params)
    const hasBody = body !== undefined
    const init: RequestInit = {
      method,
      headers: buildHeaders(hasBody ? { 'Content-Type': 'application/json' } : {}),
      body: hasBody ? JSON.stringify(body) : undefined,
    }

    try {
      const res = await fetch(url, init)
      return handleResponse<T>(res, url, init)
    } catch (err) {
      if (err instanceof Error && err.message.startsWith('HTTP ')) throw err
      if (err instanceof Error && err.message.startsWith('Unauthorized')) throw err
      const error = err instanceof Error ? err : new Error(String(err))
      on_error?.(error, url, init)
      throw error
    }
  }

  return {
    get: <T>(path: string, params?: Record<string, string>) =>
      request<T>('GET', path, undefined, params),
    post: <T>(path: string, body?: unknown) => request<T>('POST', path, body),
    put: <T>(path: string, body?: unknown) => request<T>('PUT', path, body),
    patch: <T>(path: string, body?: unknown) => request<T>('PATCH', path, body),
    del: <T>(path: string) => request<T>('DELETE', path),
  }
}
