import axios, { type AxiosError, type InternalAxiosRequestConfig } from 'axios'
import { useAuthStore } from '@/store/authStore'
import { toast } from '@/lib/toast'

const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'

export const apiClient = axios.create({
  baseURL: `${API_BASE_URL}/api/v1`,
  headers: {
    'Content-Type': 'application/json',
  },
})

// Global error handler for shared cases only.
// By convention, 4xx errors are handled at the page/component level.
function handleGlobalError(error: AxiosError) {
  // Network error (no response from server)
  if (!error.response) {
    if (error.code === 'ECONNABORTED') {
      toast.error('Request timeout - please try again')
    } else {
      toast.error('Network error - please check your connection')
    }
    return
  }

  const status = error.response.status
  const data = error.response.data as { detail?: string | unknown[] }

  // Extract a displayable error message from the response.
  // FastAPI returns `detail` as a string for most errors, but 422 validation
  // errors return an array of objects — we must not pass those to toast.
  const detail = typeof data?.detail === 'string' ? data.detail : undefined

  // Don't show toast for 401 (handled by refresh interceptor)
  if (status === 401) {
    return
  }

  // Rate limit — always worth notifying
  if (status === 429) {
    toast.error(detail || 'Too many requests — please try again shortly')
    return
  }

  // Client errors (4xx) remain page-owned to avoid duplicate/noisy toasts.
  // Global handling only covers 401/429/5xx.
  if (status >= 400 && status < 500) {
    return
  }

  // Server errors (5xx)
  if (status >= 500) {
    toast.error('Server error - please try again later')
    return
  }
}

// Request interceptor - add auth token
apiClient.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    const token = localStorage.getItem('access_token')
    if (token && config.headers) {
      config.headers.Authorization = `Bearer ${token}`
    }
    return config
  },
  (error) => Promise.reject(error)
)

// Refresh queue: when multiple requests hit 401 simultaneously,
// only the first triggers the actual refresh. Others wait for the result.
let isRefreshing = false
let refreshSubscribers: ((token: string) => void)[] = []
let refreshFailSubscribers: ((error: unknown) => void)[] = []

function onRefreshed(token: string) {
  refreshSubscribers.forEach(cb => cb(token))
  refreshSubscribers = []
  refreshFailSubscribers = []
}

function onRefreshFailed(error: unknown) {
  refreshFailSubscribers.forEach(cb => cb(error))
  refreshSubscribers = []
  refreshFailSubscribers = []
}

// Response interceptor - handle token refresh
apiClient.interceptors.response.use(
  (response) => response,
  async (error: AxiosError) => {
    const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean }

    // Only handle 401s that haven't already been retried
    if (error.response?.status !== 401 || originalRequest._retry) {
      // Show global error toast for non-401 errors
      // Pages can still catch errors explicitly for custom handling
      handleGlobalError(error)
      return Promise.reject(error)
    }

    originalRequest._retry = true

    const refreshToken = localStorage.getItem('refresh_token')
    if (!refreshToken) {
      return Promise.reject(error)
    }

    // If a refresh is already in progress, queue this request
    if (isRefreshing) {
      return new Promise((resolve, reject) => {
        refreshSubscribers.push((newToken: string) => {
          if (originalRequest.headers) {
            originalRequest.headers.Authorization = `Bearer ${newToken}`
          }
          resolve(apiClient(originalRequest))
        })
        refreshFailSubscribers.push((refreshError: unknown) => {
          reject(refreshError)
        })
      })
    }

    // This is the first 401 — perform the refresh
    isRefreshing = true

    try {
      const response = await axios.post(`${API_BASE_URL}/api/v1/auth/refresh`, null, {
        headers: {
          Authorization: `Bearer ${refreshToken}`,
        },
      })

      const { access_token, refresh_token } = response.data
      localStorage.setItem('access_token', access_token)
      localStorage.setItem('refresh_token', refresh_token)

      // Sync Zustand auth store
      useAuthStore.getState().setTokens({
        access_token,
        refresh_token,
        token_type: 'bearer',
      })

      isRefreshing = false
      onRefreshed(access_token)

      // Retry original request with new token
      if (originalRequest.headers) {
        originalRequest.headers.Authorization = `Bearer ${access_token}`
      }
      return apiClient(originalRequest)
    } catch (refreshError) {
      isRefreshing = false
      onRefreshFailed(refreshError)

      // Refresh failed — clear tokens and redirect to login
      localStorage.removeItem('access_token')
      localStorage.removeItem('refresh_token')
      useAuthStore.getState().logout()
      window.location.href = '/login'
      return Promise.reject(refreshError)
    }
  }
)

export default apiClient
