import axios, {
  AxiosError,
  AxiosHeaders,
  type InternalAxiosRequestConfig,
} from "axios";
import { Platform } from "react-native";
import { apiConfig } from "../config/api";
import type { ApiEnvelope, RefreshData } from "../types/auth";
import { clearSession, readSession, saveSession } from "./tokenStorage";

const apiClient = axios.create({
  baseURL: apiConfig.baseUrl,
  timeout: apiConfig.timeoutMs,
});

let hydrated = false;
let accessTokenCache: string | null = null;
let refreshTokenCache: string | null = null;
let refreshPromise: Promise<string | null> | null = null;

function buildDevicePayload() {
  return {
    device_id: `mobile-${Platform.OS}`,
    device_name: Platform.OS,
    platform: Platform.OS,
  };
}

type RetryableRequest = InternalAxiosRequestConfig & { _retry?: boolean };

function ensureHeaders(config: InternalAxiosRequestConfig): AxiosHeaders {
  if (!config.headers) {
    config.headers = new AxiosHeaders();
  }

  if (config.headers instanceof AxiosHeaders) {
    return config.headers;
  }

  const headers = new AxiosHeaders();
  headers.set(config.headers as Record<string, string>);
  config.headers = headers;
  return headers;
}

function getHeader(config: InternalAxiosRequestConfig, name: string): string {
  const headers = ensureHeaders(config);
  const value = headers.get(name);
  return typeof value === "string" ? value : "";
}

function setHeader(config: InternalAxiosRequestConfig, name: string, value: string): void {
  ensureHeaders(config).set(name, value);
}

function deleteHeader(config: InternalAxiosRequestConfig, name: string): void {
  ensureHeaders(config).delete(name);
}

async function hydrateTokensFromStorage(): Promise<void> {
  if (hydrated) {
    return;
  }
  const session = await readSession();
  accessTokenCache = session.accessToken;
  refreshTokenCache = session.refreshToken;
  hydrated = true;
}

async function refreshAccessToken(): Promise<string | null> {
  if (!refreshTokenCache) {
    return null;
  }

  try {
    const response = await axios.post<ApiEnvelope<RefreshData>>(
      `${apiConfig.baseUrl}/refresh.php`,
      {
        refresh_token: refreshTokenCache,
        ...buildDevicePayload(),
      },
      { timeout: apiConfig.timeoutMs }
    );

    if (!response.data?.success || !response.data.data) {
      throw new Error(response.data?.message || "Refresh failed");
    }

    const refreshed = response.data.data;
    accessTokenCache = refreshed.access_token;
    refreshTokenCache = refreshed.refresh_token;
    hydrated = true;

    await saveSession(
      refreshed.access_token,
      refreshed.refresh_token,
      refreshed.user
    );

    return refreshed.access_token;
  } catch {
    await clearSession();
    accessTokenCache = null;
    refreshTokenCache = null;
    hydrated = true;
    return null;
  }
}

apiClient.interceptors.request.use(async (config) => {
  await hydrateTokensFromStorage();

  const skipAuth = getHeader(config, "X-Skip-Auth") === "1";
  deleteHeader(config, "X-Skip-Auth");

  if (!skipAuth && accessTokenCache && getHeader(config, "Authorization") === "") {
    setHeader(config, "Authorization", `Bearer ${accessTokenCache}`);
  }

  return config;
});

apiClient.interceptors.response.use(
  (response) => response,
  async (error: AxiosError) => {
    const originalRequest = error.config as RetryableRequest | undefined;

    if (!originalRequest || error.response?.status !== 401) {
      return Promise.reject(error);
    }

    const skipRefresh = getHeader(originalRequest, "X-Skip-Refresh") === "1";
    deleteHeader(originalRequest, "X-Skip-Refresh");

    if (skipRefresh || originalRequest._retry) {
      return Promise.reject(error);
    }

    await hydrateTokensFromStorage();
    if (!refreshTokenCache) {
      return Promise.reject(error);
    }

    if (!refreshPromise) {
      refreshPromise = refreshAccessToken().finally(() => {
        refreshPromise = null;
      });
    }

    const newAccessToken = await refreshPromise;
    if (!newAccessToken) {
      return Promise.reject(error);
    }

    originalRequest._retry = true;
    setHeader(originalRequest, "Authorization", `Bearer ${newAccessToken}`);

    return apiClient(originalRequest);
  }
);

export async function initializeApiClientAuth(): Promise<void> {
  await hydrateTokensFromStorage();
}

export function setApiAuthTokens(
  accessToken: string | null,
  refreshToken: string | null
): void {
  accessTokenCache = accessToken;
  refreshTokenCache = refreshToken;
  hydrated = true;
}

export function clearApiAuthTokens(): void {
  accessTokenCache = null;
  refreshTokenCache = null;
  hydrated = true;
}

export default apiClient;
