export interface ApiResponse<T = unknown> {
  code: number;
  message: string;
  data: T;
  meta?: { page: number; page_size: number; total: number; has_more: boolean };
}

export interface PaginationParams {
  page?: number;
  page_size?: number;
}

const API_BASE = '/api/v1';

let accessToken: string | null = null;
let refreshTokenValue: string | null = null;
let refreshPromise: Promise<boolean> | null = null;

export function setAccessToken(token: string | null) {
  accessToken = token;
}

export function setRefreshToken(token: string | null) {
  refreshTokenValue = token;
}

export function getAccessToken(): string | null {
  return accessToken;
}

async function refreshToken(): Promise<boolean> {
  try {
    const res = await fetch(`${API_BASE}/auth/refresh`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ refresh_token: refreshTokenValue }),
      credentials: 'include',
    });
    if (!res.ok) return false;
    const body: ApiResponse<{ access_token: string }> = await res.json();
    if (body.code === 0 && body.data.access_token) {
      accessToken = body.data.access_token;
      return true;
    }
    return false;
  } catch {
    return false;
  }
}

async function request<T>(
  method: string,
  path: string,
  body?: unknown,
  params?: Record<string, string | number | undefined>,
): Promise<ApiResponse<T>> {
  let url = `${API_BASE}${path}`;

  if (params) {
    const qs = Object.entries(params)
      .filter(([, v]) => v !== undefined)
      .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
      .join('&');
    if (qs) url += `?${qs}`;
  }

  const headers: Record<string, string> = {};
  if (accessToken) headers['Authorization'] = `Bearer ${accessToken}`;
  if (body !== undefined) headers['Content-Type'] = 'application/json';

  let res = await fetch(url, {
    method,
    headers,
    body: body !== undefined ? JSON.stringify(body) : undefined,
    credentials: 'include',
  });

  if (res.status === 401 && accessToken) {
    if (!refreshPromise) refreshPromise = refreshToken();
    const ok = await refreshPromise;
    refreshPromise = null;

    if (ok) {
      headers['Authorization'] = `Bearer ${accessToken}`;
      res = await fetch(url, {
        method,
        headers,
        body: body !== undefined ? JSON.stringify(body) : undefined,
        credentials: 'include',
      });
    } else {
      accessToken = null;
      window.dispatchEvent(new CustomEvent('auth:expired'));
    }
  }

  const json: ApiResponse<T> = await res.json();
  if (json.code !== 0) {
    throw new ApiError(json.code, json.message);
  }
  return json;
}

export class ApiError extends Error {
  constructor(public code: number, message: string) {
    super(message);
    this.name = 'ApiError';
  }
}

export const http = {
  get: <T>(path: string, params?: Record<string, string | number | undefined>) =>
    request<T>('GET', path, undefined, params),

  post: <T>(path: string, body?: unknown) =>
    request<T>('POST', path, body),

  put: <T>(path: string, body?: unknown) =>
    request<T>('PUT', path, body),

  patch: <T>(path: string, body?: unknown) =>
    request<T>('PATCH', path, body),

  delete: <T>(path: string) =>
    request<T>('DELETE', path),
};
