import axios, { type AxiosRequestConfig } from "axios";
import { env } from "@/infrastructure/config/env";
import { tokenStorage } from "@/infrastructure/auth/token-storage";
import { authEvents } from "@/infrastructure/auth/auth-events";
import type { ApiResponse } from "@/shared/types";

// No global `Content-Type` on purpose: axios picks `application/json` for plain-object
// bodies by itself, and a forced JSON header makes it JSON-stringify FormData payloads
// (File objects serialise to `{}`, so the backend rejects uploads with "must be a file").
// With the header left unset, FormData goes out as multipart/form-data with a boundary.
export const apiClient = axios.create({
  baseURL: env.apiUrl,
  timeout: 30000,
  headers: {
    Accept: "application/json",
  },
});

apiClient.interceptors.request.use(
  (config) => {
    const token = tokenStorage.get();
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

apiClient.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      tokenStorage.clear();
      authEvents.emitUnauthorized();
    }
    return Promise.reject(error);
  }
);

/**
 * Typed helpers that unwrap the standard `ApiResponse<T>` envelope.
 * Use these in repository implementations to remove boilerplate.
 * For raw responses (e.g. login, where the envelope itself matters), use `apiClient` directly.
 */
export const api = {
  get: async <T>(url: string, config?: AxiosRequestConfig): Promise<T> => {
    const { data } = await apiClient.get<ApiResponse<T>>(url, config);
    return data.data;
  },
  post: async <T>(url: string, body?: unknown, config?: AxiosRequestConfig): Promise<T> => {
    const { data } = await apiClient.post<ApiResponse<T>>(url, body, config);
    return data.data;
  },
  put: async <T>(url: string, body?: unknown, config?: AxiosRequestConfig): Promise<T> => {
    const { data } = await apiClient.put<ApiResponse<T>>(url, body, config);
    return data.data;
  },
  patch: async <T>(url: string, body?: unknown, config?: AxiosRequestConfig): Promise<T> => {
    const { data } = await apiClient.patch<ApiResponse<T>>(url, body, config);
    return data.data;
  },
  delete: async (url: string, config?: AxiosRequestConfig): Promise<void> => {
    await apiClient.delete(url, config);
  },
};
