import axios from "axios";
import {
  authorizationHeader,
  clearTokens,
  getAccessToken,
  getRefreshToken,
  setTokens,
} from "./llm-fe/auth/tokenStorage";
import {
  decodeJwtPayload,
  isTokenNotValidError,
} from "./llm-fe/auth/jwtHelpers";
import { redirectToAppPath } from "./llm-fe/platform/nativePlatform";

const baseURL = process.env.REACT_APP_BACKEND_REST_API_BASE_URL;

/**
 * Shared JWT axios client — Authorization bearer only, no cookies/CSRF (#22).
 * Works for browser + Capacitor WebView origins (https://localhost / capacitor://localhost).
 */
export const axiosInstance = axios.create({
  baseURL: baseURL,
  timeout: 5000,
  headers: {
    "Content-Type": "application/json",
    Accept: "application/json",
  },
  withCredentials: false,
});

export const cleanAxiosInstance = axios.create({
  baseURL: baseURL,
  timeout: 5000,
  headers: {
    "Content-Type": "application/json",
    Accept: "application/json",
  },
  withCredentials: false,
});

/**
 * Apply access token to axios defaults (login / refresh).
 * @param {string|null} access
 */
export function applyAccessToken(access) {
  const header = authorizationHeader(access);
  if (header) {
    axiosInstance.defaults.headers.common["Authorization"] = header;
  } else {
    delete axiosInstance.defaults.headers.common["Authorization"];
  }
}

applyAccessToken(getAccessToken());

axiosInstance.interceptors.request.use((config) => {
  config.timeout = 100000;
  config.withCredentials = false;
  const header = authorizationHeader(getAccessToken());
  if (header) {
    config.headers = config.headers || {};
    config.headers.Authorization = header;
  } else if (config.headers) {
    delete config.headers.Authorization;
  }
  return config;
});

/**
 * @param {string} refreshToken
 * @returns {Promise<{ access: string, refresh?: string }>}
 */
export async function refreshAccessToken(refreshToken) {
  const response = await axiosInstance.post("/token/refresh/", {
    refresh: refreshToken,
  });
  await setTokens(response.data.access, response.data.refresh ?? refreshToken);
  applyAccessToken(response.data.access);
  return response.data;
}

export { decodeJwtPayload, isTokenNotValidError };

axiosInstance.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;
    if (!error.response || !originalRequest) {
      return Promise.reject(error);
    }

    const requestUrl = originalRequest.url || "";
    const isRefreshCall =
      requestUrl.includes("/token/refresh/") ||
      requestUrl === `${baseURL}/token/refresh/`;

    if (error.response.status === 401 && isRefreshCall) {
      await clearTokens();
      applyAccessToken(null);
      // Hash-safe on Capacitor (#24); plain path on web BrowserRouter.
      redirectToAppPath("/signin/");
      return Promise.reject(error);
    }

    if (isTokenNotValidError(error) && !originalRequest._retry) {
      originalRequest._retry = true;
      const refresh_token = getRefreshToken();

      if (!refresh_token) {
        await clearTokens();
        applyAccessToken(null);
        redirectToAppPath("/signin/");
        return Promise.reject(error);
      }

      const tokenParts = decodeJwtPayload(refresh_token);
      const now = Math.ceil(Date.now() / 1000);

      if (tokenParts?.exp != null && tokenParts.exp > now) {
        try {
          const data = await refreshAccessToken(refresh_token);
          originalRequest.headers = originalRequest.headers || {};
          originalRequest.headers.Authorization = authorizationHeader(
            data.access,
          );
          return axiosInstance(originalRequest);
        } catch (err) {
          await clearTokens();
          applyAccessToken(null);
          redirectToAppPath("/signin/");
          return Promise.reject(err);
        }
      }

      await clearTokens();
      applyAccessToken(null);
      redirectToAppPath("/signin/");
    }

    return Promise.reject(error);
  },
);
