/* eslint-disable @typescript-eslint/no-explicit-any */
import axios, { AxiosRequestConfig, AxiosError } from "axios";

import { Roles } from "@/entities";

import { useIdentityStore } from "../features/Login/lib/identity.store";


const axiosInstance = axios.create({
	// Включаем cookies для запросов
	withCredentials: true,
});

interface RefreshTokenResponse {
	id: string;
	username: string;
	role: Roles;
}

// Добавляем перехватчик ответов для обработки 401 ошибок (Unauthorized)
axiosInstance.interceptors.response.use(
	(response) => response,
	async (error: AxiosError) => {
		// Если ошибка не 401 или уже обрабатываем refreshToken, просто возвращаем ошибку
		const originalRequest = error.config;
		if (
			!error.response ||
			error.response.status !== 401 ||
			originalRequest?.url?.includes("/api/Identity/refresh-token")
		) {
			return Promise.reject(error);
		}

		try {
			// Пытаемся обновить токен
			const response = await axios.post<RefreshTokenResponse>(
				"/api/identity/refresh-token",
				{},
				{ withCredentials: true },
			);

			// Обновляем информацию о пользователе в хранилище
			if (response.data) {
				useIdentityStore.getState().login({
					id: response.data.id,
					username: response.data.username,
					role: response.data.role,
				});
			}

			// Повторяем оригинальный запрос
			return axiosInstance(originalRequest!);
		} catch (refreshError) {
			// Если не удалось обновить токен, выходим
			useIdentityStore.getState().logout();
			return Promise.reject(refreshError);
		}
	},
);

const get = async <T>(url: string, params?: Record<string, unknown>) => {
	const response = await axiosInstance.get<T>(url, { params });
	return response.data;
};

const post = async <TResponse, TRequest = unknown>(
	url: string,
	data?: TRequest,
	config?: AxiosRequestConfig<any>,
) => {
	const response = await axiosInstance.post<TResponse>(url, data, config);
	return response.data;
};

const put = async <TResponse, TRequest = unknown>(
	url: string,
	data?: TRequest,
) => {
	const response = await axiosInstance.put<TResponse>(url, data);
	return response.data;
};

const remove = async <TResponse = unknown, TRequest = unknown>(
	url: string,
	data?: TRequest,
) => {
	const response = await axiosInstance.delete<TResponse>(url, { data });
	return response.data;
};

export const http = {
	get,
	post,
	put,
	remove,
};

export const customClient = async <T>(
	config: AxiosRequestConfig,
): Promise<T> => {
	// Здесь выполняется сам запрос с использованием настроенного экземпляра Axios
	const promise = axiosInstance.request<T>(config);

	// orval ожидает Promise, который резолвится с ДАННЫМИ ответа (response.data)
	// Поэтому мы трансформируем результат промиса Axios
	return promise.then((response) => response.data);
};
