import axios, { AxiosError, AxiosRequestConfig, InternalAxiosRequestConfig } from 'axios';

export interface ApiError {
  status: number;
  message: string;
  detail?: string;
  validationErrors?: Record<string, string[]>;
}

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

// CSRF token storage — persisted in sessionStorage, sent on all unsafe methods
const CSRF_KEY = 'leocrm_csrf_token';
let csrfToken: string | null = sessionStorage.getItem(CSRF_KEY);

export function setCsrfToken(token: string | null) {
  csrfToken = token;
  if (token) {
    sessionStorage.setItem(CSRF_KEY, token);
  } else {
    sessionStorage.removeItem(CSRF_KEY);
  }
}

export function getCsrfToken(): string | null {
  return csrfToken;
}

let onUnauthorized: (() => void) | null = null;
let onValidationError: ((errors: Record<string, string[]>) => void) | null = null;

export function setUnauthorizedHandler(handler: () => void) {
  onUnauthorized = handler;
}

export function setValidationErrorHandler(handler: (errors: Record<string, string[]>) => void) {
  onValidationError = handler;
}

apiClient.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // Attach CSRF token on unsafe methods
    const unsafe = ['post', 'put', 'patch', 'delete'];
    if (unsafe.includes(config.method?.toLowerCase() ?? '') && csrfToken) {
      config.headers['X-CSRF-Token'] = csrfToken;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

apiClient.interceptors.response.use(
  (response) => response,
  (error: AxiosError) => {
    const status = error.response?.status || 0;
    const data = error.response?.data as any;

    if (status === 401) {
      if (onUnauthorized) {
        onUnauthorized();
      }
    }

    if (status === 422 && data?.detail) {
      const validationErrors: Record<string, string[]> = {};
      if (Array.isArray(data.detail)) {
        for (const item of data.detail) {
          if (item.loc && item.loc.length > 1) {
            const field = item.loc[item.loc.length - 1];
            if (!validationErrors[field]) {
              validationErrors[field] = [];
            }
            validationErrors[field].push(item.msg || 'Invalid value');
          }
        }
      }
      if (onValidationError) {
        onValidationError(validationErrors);
      }
    }

    const apiError: ApiError = {
      status,
      message: data?.detail?.message || data?.message || error.message || 'An error occurred',
      detail: typeof data?.detail === 'string' ? data.detail : undefined,
      validationErrors: status === 422 ? extractValidationErrors(data) : undefined,
    };

    return Promise.reject(apiError);
  }
);

function extractValidationErrors(data: any): Record<string, string[]> | undefined {
  if (!data?.detail || !Array.isArray(data.detail)) return undefined;
  const errors: Record<string, string[]> = {};
  for (const item of data.detail) {
    if (item.loc && item.loc.length > 1) {
      const field = item.loc[item.loc.length - 1];
      if (!errors[field]) {
        errors[field] = [];
      }
      errors[field].push(item.msg || 'Invalid value');
    }
  }
  return Object.keys(errors).length > 0 ? errors : undefined;
}

export async function apiGet<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
  const response = await apiClient.get<T>(url, config);
  return response.data;
}

export async function apiPost<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
  const response = await apiClient.post<T>(url, data, config);
  return response.data;
}

export async function apiPut<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
  const response = await apiClient.put<T>(url, data, config);
  return response.data;
}

export async function apiPatch<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
  const response = await apiClient.patch<T>(url, data, config);
  return response.data;
}

export async function apiDelete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
  const response = await apiClient.delete<T>(url, config);
  return response.data;
}

export default apiClient;
