const API_BASE = import.meta.env.VITE_API_BASE_URL || "http://localhost:8080/api";

let accessToken =
  (typeof window !== "undefined" && localStorage.getItem("br_access_token")) || "";

let refreshPromise = null;

if (typeof window !== "undefined") {
  window.__br_access_token = accessToken || "";
}

export function setAccessToken(token) {
  accessToken = token || "";

  if (typeof window !== "undefined") {
    if (accessToken) {
      localStorage.setItem("br_access_token", accessToken);
    } else {
      localStorage.removeItem("br_access_token");
    }
    window.__br_access_token = accessToken;
  }
}

export function clearAuthState() {
  accessToken = "";

  if (typeof window !== "undefined") {
    localStorage.removeItem("br_access_token");
    localStorage.removeItem("br_user");
    window.__br_access_token = "";
  }
}

function ensureAccessToken() {
  if (!accessToken && typeof window !== "undefined") {
    accessToken = localStorage.getItem("br_access_token") || "";
    window.__br_access_token = accessToken;
  }
  return accessToken;
}

function shouldSendJsonContentType(options = {}) {
  if (!options.body) return false;
  if (options.body instanceof FormData) return false;
  if (typeof options.body === "string") return true;
  return false;
}

function buildHeaders(options = {}, tokenOverride = null) {
  const headers = {
    ...(shouldSendJsonContentType(options) ? { "Content-Type": "application/json" } : {}),
    ...(options.headers || {}),
  };

  const token = tokenOverride ?? ensureAccessToken();
  if (token) {
    headers.Authorization = `Bearer ${token}`;
  }

  return headers;
}

async function doRefreshAccessToken() {
  const refreshed = await fetch(`${API_BASE}/auth/refresh`, {
    method: "POST",
    credentials: "include",
  });

  if (!refreshed.ok) {
    clearAuthState();
    return null;
  }

  const refreshData = await refreshed.json().catch(() => ({}));
  const newToken = refreshData?.accessToken || "";

  if (!newToken) {
    clearAuthState();
    return null;
  }

  setAccessToken(newToken);

  if (typeof window !== "undefined" && refreshData?.user) {
    localStorage.setItem("br_user", JSON.stringify(refreshData.user));
  }

  return newToken;
}

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

  return refreshPromise;
}

async function parseJsonSafe(response) {
  return response.json().catch(() => ({}));
}

export async function request(path, options = {}) {
  let response = await fetch(`${API_BASE}${path}`, {
    ...options,
    headers: buildHeaders(options),
    credentials: "include",
  });

  if (response.status === 401 && !path.includes("/auth/refresh")) {
    const refreshedToken = await refreshAccessToken();

    if (refreshedToken) {
      response = await fetch(`${API_BASE}${path}`, {
        ...options,
        headers: buildHeaders(options, refreshedToken),
        credentials: "include",
      });
    }
  }

  const data = await parseJsonSafe(response);

  if (!response.ok) {
    if (response.status === 401) {
      clearAuthState();
    }
    const err = new Error(data.error || "API-Fehler");
    err.response = data;
    err.status = response.status;
    throw err;
  }

  return data;
}

export async function requestBlob(path, options = {}) {
  let response = await fetch(`${API_BASE}${path}`, {
    ...options,
    headers: buildHeaders(options),
    credentials: "include",
  });

  if (response.status === 401 && !path.includes("/auth/refresh")) {
    const refreshedToken = await refreshAccessToken();

    if (refreshedToken) {
      response = await fetch(`${API_BASE}${path}`, {
        ...options,
        headers: buildHeaders(options, refreshedToken),
        credentials: "include",
      });
    }
  }

  if (!response.ok) {
    const data = await parseJsonSafe(response);
    if (response.status === 401) {
      clearAuthState();
    }
    const err = new Error(data.error || "Datei konnte nicht geladen werden");
    err.response = data;
    err.status = response.status;
    throw err;
  }

  return response;
}

export async function downloadBinary(path, suggestedFilename = "download") {
  const response = await requestBlob(path);
  const blob = await response.blob();
  const objectUrl = window.URL.createObjectURL(blob);

  const link = document.createElement("a");
  link.href = objectUrl;
  link.download = suggestedFilename;
  document.body.appendChild(link);
  link.click();
  link.remove();

  window.setTimeout(() => window.URL.revokeObjectURL(objectUrl), 1000);
}

export async function openBinary(path) {
  const response = await requestBlob(path);
  const blob = await response.blob();
  const objectUrl = window.URL.createObjectURL(blob);

  window.open(objectUrl, "_blank", "noopener,noreferrer");

  window.setTimeout(() => window.URL.revokeObjectURL(objectUrl), 60_000);
}

export async function printBinary(path) {
  const response = await requestBlob(path);
  const blob = await response.blob();
  const objectUrl = window.URL.createObjectURL(blob);

  const iframe = document.createElement("iframe");
  iframe.style.position = "fixed";
  iframe.style.right = "0";
  iframe.style.bottom = "0";
  iframe.style.width = "0";
  iframe.style.height = "0";
  iframe.style.border = "0";
  iframe.src = objectUrl;

  iframe.onload = () => {
    window.setTimeout(() => {
      try {
        iframe.contentWindow?.focus();
        iframe.contentWindow?.print();
      } catch (err) {
        window.open(objectUrl, "_blank", "noopener,noreferrer");
      }
    }, 250);

    window.setTimeout(() => {
      window.URL.revokeObjectURL(objectUrl);
      iframe.remove();
    }, 60_000);
  };

  document.body.appendChild(iframe);
}

export const api = {
  get: (path) => request(path),
  post: (path, body) =>
    request(path, {
      method: "POST",
      body: body instanceof FormData ? body : JSON.stringify(body),
    }),
  put: (path, body) =>
    request(path, {
      method: "PUT",
      body: body instanceof FormData ? body : JSON.stringify(body),
    }),
  delete: (path) =>
    request(path, {
      method: "DELETE",
    }),
};
