import axios, { type AxiosError } from "axios";
import type { ApiResponse } from "./types";
import { getOrCreateTerminalId } from "@/lib/terminal";

const baseURL =
  process.env.NEXT_PUBLIC_API_URL ?? "https://localhost:7208";

export const api = axios.create({
  baseURL,
  headers: { "Content-Type": "application/json" },
});

api.interceptors.request.use((config) => {
  if (typeof window !== "undefined") {
    const token = localStorage.getItem("meezi_access_token");
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    config.headers["X-Meezi-Terminal-Id"] = getOrCreateTerminalId();
  }
  return config;
});

api.interceptors.response.use(
  (response) => response,
  async (error: AxiosError<ApiResponse<unknown>>) => {
    const apiError = error.response?.data?.error;
    if (apiError?.code) {
      return Promise.reject(new ApiClientError(apiError.code, apiError.message));
    }
    if (error.response?.status === 401 && typeof window !== "undefined") {
      const path = window.location.pathname;
      const isPublicGuest = path.startsWith("/q/") || path.startsWith("/q");
      const isAdmin = path.includes("/admin");
      if (!isPublicGuest && !isAdmin) {
        localStorage.removeItem("meezi_access_token");
        localStorage.removeItem("meezi_refresh_token");
        localStorage.removeItem("meezi_auth");
        const locale = path.split("/")[1] ?? "fa";
        window.location.href = `/${locale}/login`;
      }
    }
    return Promise.reject(error);
  }
);

export interface PagedMeta {
  total: number;
  page: number;
  pageSize: number;
}

export interface PagedApiResponse<T> {
  success: boolean;
  data?: T[];
  meta?: PagedMeta;
  error?: { code: string; message: string; field?: string };
}

export async function apiGet<T>(url: string): Promise<T> {
  const { data } = await api.get<ApiResponse<T>>(url);
  if (!data.success || data.data === undefined) {
    throw new Error(data.error?.message ?? "Request failed");
  }
  return data.data;
}

export async function apiGetPaged<T>(url: string): Promise<{ items: T[]; meta: PagedMeta }> {
  const { data } = await api.get<PagedApiResponse<T>>(url);
  if (!data.success || data.data === undefined || !data.meta) {
    throw new Error(data.error?.message ?? "Request failed");
  }
  return { items: data.data, meta: data.meta };
}

export class ApiClientError extends Error {
  constructor(
    public readonly code: string,
    message: string,
    /** Payload returned alongside a non-success response (e.g. CHOOSE_CAFE choices). */
    public readonly payload?: unknown
  ) {
    super(message);
    this.name = "ApiClientError";
  }
}

export async function apiPost<T, B = unknown>(url: string, body?: B): Promise<T> {
  const { data } = await api.post<ApiResponse<T>>(url, body);
  if (!data.success || data.data === undefined) {
    const code = data.error?.code ?? "REQUEST_FAILED";
    throw new ApiClientError(code, data.error?.message ?? "Request failed", data.data);
  }
  return data.data;
}

export async function apiPut<T, B = unknown>(url: string, body?: B): Promise<T> {
  const { data } = await api.put<ApiResponse<T>>(url, body);
  if (!data.success || data.data === undefined) {
    const code = data.error?.code ?? "REQUEST_FAILED";
    throw new ApiClientError(code, data.error?.message ?? "Request failed");
  }
  return data.data;
}

export async function apiPatch<T, B = unknown>(url: string, body?: B): Promise<T> {
  const { data } = await api.patch<ApiResponse<T>>(url, body);
  if (!data.success || data.data === undefined) {
    const code = data.error?.code ?? "REQUEST_FAILED";
    throw new ApiClientError(code, data.error?.message ?? "Request failed");
  }
  return data.data;
}

export async function apiDelete(url: string): Promise<void> {
  const { data } = await api.delete<ApiResponse<unknown>>(url);
  if (!data.success) {
    const code = data.error?.code ?? "REQUEST_FAILED";
    throw new ApiClientError(code, data.error?.message ?? "Request failed");
  }
}

/** GET binary response (QR PNG, Excel export, etc.) with auth headers. */
export async function apiGetBlob(path: string): Promise<Blob> {
  const response = await api.get(path, { responseType: "blob" });
  return response.data as Blob;
}

/** Public GET JSON (no auth required). */
export async function apiGetPublic<T>(path: string): Promise<T> {
  const { data } = await api.get<ApiResponse<T>>(path);
  if (!data.success || data.data === undefined) {
    throw new ApiClientError(
      data.error?.code ?? "REQUEST_FAILED",
      data.error?.message ?? "Request failed"
    );
  }
  return data.data;
}

/** Public POST JSON (no auth required). */
export async function apiPostPublic<T, B = unknown>(path: string, body?: B): Promise<T> {
  const { data } = await api.post<ApiResponse<T>>(path, body);
  if (!data.success || data.data === undefined) {
    throw new ApiClientError(
      data.error?.code ?? "REQUEST_FAILED",
      data.error?.message ?? "Request failed"
    );
  }
  return data.data;
}

export function openBlobInNewTab(blob: Blob): void {
  const url = URL.createObjectURL(blob);
  window.open(url, "_blank");
}

export async function apiDownload(path: string, filename: string): Promise<void> {
  const blob = await apiGetBlob(path);
  const objectUrl = URL.createObjectURL(blob);
  const anchor = document.createElement("a");
  anchor.href = objectUrl;
  anchor.download = filename;
  anchor.click();
  URL.revokeObjectURL(objectUrl);
}

export async function apiUpload<T>(url: string, file: File): Promise<T> {
  const form = new FormData();
  form.append("file", file);
  const { data } = await api.post<ApiResponse<T>>(url, form, {
    headers: { "Content-Type": "multipart/form-data" },
  });
  if (!data.success || data.data === undefined) {
    throw new Error(data.error?.message ?? "Upload failed");
  }
  return data.data;
}

export function resolveMediaUrl(path?: string | null): string | undefined {
  if (!path) return undefined;
  if (path.startsWith("http://") || path.startsWith("https://")) return path;
  const base = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:5080";
  return `${base.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
}
