/** Base path or absolute URL for Odoo JSON API (dev: `/api` + Vite proxy). */
export const API_BASE_URL = (import.meta.env.VITE_API_BASE_URL?.trim() || "/api").replace(/\/$/, "");
const BASE_URL = API_BASE_URL;

export function extractApiErrorMessage(data: unknown, status: number, statusText: string): string {
  if (data && typeof data === "object" && data !== null && "error" in data) {
    const msg = (data as { error: unknown }).error;
    if (msg != null && String(msg).trim()) return String(msg);
  }
  return `${status} ${statusText}`;
}

export class ApiError extends Error {
  constructor(
    public status: number,
    public statusText: string,
    public data: unknown,
  ) {
    super(extractApiErrorMessage(data, status, statusText));
    this.name = "ApiError";
  }
}

function getToken(): string | null {
  return localStorage.getItem("encoach_token");
}

export function setToken(token: string): void {
  localStorage.setItem("encoach_token", token);
}

export function clearToken(): void {
  localStorage.removeItem("encoach_token");
}

async function handleResponse<T>(response: Response): Promise<T> {
  const data = await response.json().catch(() => null);

  if (response.status === 401) {
    const hadToken = !!getToken();
    clearToken();
    // Login failure is also 401 — do not hard-redirect when no session existed.
    if (hadToken) {
      window.location.href = "/login";
    }
    throw new ApiError(401, response.statusText, data);
  }

  if (!response.ok) {
    throw new ApiError(response.status, response.statusText, data);
  }

  return data as T;
}

function buildHeaders(extra?: Record<string, string>): Record<string, string> {
  const headers: Record<string, string> = {
    "Content-Type": "application/json",
    ...extra,
  };

  const token = getToken();
  if (token) {
    headers["Authorization"] = `Bearer ${token}`;
  }

  return headers;
}

function buildUrl(path: string, params?: Record<string, string | number | boolean | undefined>): string {
  const url = new URL(`${BASE_URL}${path}`, window.location.origin);
  if (params) {
    Object.entries(params).forEach(([key, value]) => {
      if (value !== undefined) {
        url.searchParams.set(key, String(value));
      }
    });
  }
  return url.toString();
}

export const api = {
  async get<T>(path: string, params?: Record<string, string | number | boolean | undefined>): Promise<T> {
    const res = await fetch(buildUrl(path, params), {
      method: "GET",
      headers: buildHeaders(),
    });
    return handleResponse<T>(res);
  },

  async post<T>(path: string, body?: unknown): Promise<T> {
    const res = await fetch(buildUrl(path), {
      method: "POST",
      headers: buildHeaders(),
      body: body ? JSON.stringify(body) : undefined,
    });
    return handleResponse<T>(res);
  },

  async patch<T>(path: string, body?: unknown): Promise<T> {
    const res = await fetch(buildUrl(path), {
      method: "PATCH",
      headers: buildHeaders(),
      body: body ? JSON.stringify(body) : undefined,
    });
    return handleResponse<T>(res);
  },

  async put<T>(path: string, body?: unknown): Promise<T> {
    const res = await fetch(buildUrl(path), {
      method: "PUT",
      headers: buildHeaders(),
      body: body ? JSON.stringify(body) : undefined,
    });
    return handleResponse<T>(res);
  },

  async delete<T>(path: string): Promise<T> {
    const res = await fetch(buildUrl(path), {
      method: "DELETE",
      headers: buildHeaders(),
    });
    return handleResponse<T>(res);
  },

  async upload<T>(path: string, formData: FormData): Promise<T> {
    const token = getToken();
    const headers: Record<string, string> = {};
    if (token) {
      headers["Authorization"] = `Bearer ${token}`;
    }

    const res = await fetch(buildUrl(path), {
      method: "POST",
      headers,
      body: formData,
    });
    return handleResponse<T>(res);
  },
};
