// ============================================================
// HTTP API 客户端
// 职责：封装 REST API 请求（auth、conversations 等）
//       内置 401 拦截 + 自动刷新 + 重试机制
// ============================================================

const API_BASE = "/api";

interface ApiResponse<T> {
  data?: T;
  error?: { code: string; message: string };
  status: number;
}

// ---- 认证回调（由 AuthProvider 注入，避免循环依赖） ----

interface AuthCallbacks {
  getAccessToken: () => string | null;
  getRefreshToken: () => string | null;
  onRefreshSuccess: (user: AuthUser, accessToken: string, refreshToken: string) => void;
  onRefreshFailed: () => void;
}

let authCallbacks: AuthCallbacks | null = null;
let refreshPromise: Promise<boolean> | null = null;

/** 由 AuthProvider 在初始化时调用，注入认证回调。 */
export function setAuthCallbacks(callbacks: AuthCallbacks): void {
  authCallbacks = callbacks;
}

/** 不需要认证的公开路径。 */
const PUBLIC_PATHS = new Set([
  "/auth/register",
  "/auth/login",
  "/auth/refresh",
]);

function isPublicPath(path: string): boolean {
  return PUBLIC_PATHS.has(path);
}

/** 尝试用 refresh token 换取新的 access token。 */
async function doRefresh(): Promise<boolean> {
  const rt = authCallbacks?.getRefreshToken();
  if (!rt) return false;

  const res = await refreshTokenDirect(rt);
  if (res.data) {
    authCallbacks?.onRefreshSuccess(
      res.data.user,
      res.data.access_token,
      res.data.refresh_token
    );
    return true;
  }

  authCallbacks?.onRefreshFailed();
  return false;
}

/** 带并发保护的刷新：多个 401 只触发一次 refresh。 */
async function refreshWithLock(): Promise<boolean> {
  if (!refreshPromise) {
    refreshPromise = doRefresh().finally(() => {
      refreshPromise = null;
    });
  }
  return refreshPromise;
}

// ---- 核心请求函数 ----

async function request<T>(
  path: string,
  options: RequestInit = {},
  _retry = false
): Promise<ApiResponse<T>> {
  const url = `${API_BASE}${path}`;
  const headers: Record<string, string> = {
    "Content-Type": "application/json",
    ...(options.headers as Record<string, string>),
  };

  // 对非公开路径自动附加 access token
  if (!isPublicPath(path) && !headers["Authorization"]) {
    const token = authCallbacks?.getAccessToken();
    if (token) {
      headers["Authorization"] = `Bearer ${token}`;
    }
  }

  try {
    const res = await fetch(url, { ...options, headers });
    const status = res.status;

    if (res.status === 204) {
      return { status };
    }

    const body = await res.json();

    // 401 拦截：尝试刷新 token 后重试（仅重试一次）
    if (res.status === 401 && !_retry && !isPublicPath(path) && authCallbacks) {
      const refreshed = await refreshWithLock();
      if (refreshed) {
        return request<T>(path, options, true);
      }
    }

    if (!res.ok) {
      return {
        error: { code: body.error || "UNKNOWN", message: body.message || "请求失败" },
        status,
      };
    }

    return { data: body as T, status };
  } catch {
    return {
      error: { code: "NETWORK_ERROR", message: "网络连接失败，请检查网络" },
      status: 0,
    };
  }
}

function authHeaders(accessToken: string): Record<string, string> {
  return { Authorization: `Bearer ${accessToken}` };
}

// ---- Auth API ----

export interface AuthUser {
  id: string;
  username: string;
  created_at: string;
}

export interface AuthResponse {
  user: AuthUser;
  access_token: string;
  refresh_token: string;
}

export async function register(
  username: string,
  password: string
): Promise<ApiResponse<AuthResponse>> {
  return request<AuthResponse>("/auth/register", {
    method: "POST",
    body: JSON.stringify({ username, password }),
  });
}

export async function login(
  username: string,
  password: string
): Promise<ApiResponse<AuthResponse>> {
  return request<AuthResponse>("/auth/login", {
    method: "POST",
    body: JSON.stringify({ username, password }),
  });
}

/** 内部用的 refresh 请求，不经过 401 拦截（避免递归）。 */
async function refreshTokenDirect(
  refresh_token: string
): Promise<ApiResponse<AuthResponse>> {
  const url = `${API_BASE}/auth/refresh`;
  try {
    const res = await fetch(url, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ refresh_token }),
    });
    const body = await res.json();
    if (!res.ok) {
      return {
        error: { code: body.error || "UNKNOWN", message: body.message || "请求失败" },
        status: res.status,
      };
    }
    return { data: body as AuthResponse, status: res.status };
  } catch {
    return {
      error: { code: "NETWORK_ERROR", message: "网络连接失败" },
      status: 0,
    };
  }
}

export async function refreshToken(
  refresh_token: string
): Promise<ApiResponse<AuthResponse>> {
  return request<AuthResponse>("/auth/refresh", {
    method: "POST",
    body: JSON.stringify({ refresh_token }),
  });
}

export async function logout(
  accessToken: string,
  refreshTokenStr: string
): Promise<ApiResponse<{ message: string }>> {
  return request<{ message: string }>("/auth/logout", {
    method: "POST",
    headers: authHeaders(accessToken),
    body: JSON.stringify({ refresh_token: refreshTokenStr }),
  });
}

// ---- Conversation API ----

export interface ConversationListItem {
  id: string;
  title: string;
  last_message: string;
  message_count: number;
  created_at: string;
  updated_at: string;
}

export interface ConversationListResponse {
  conversations: ConversationListItem[];
  total: number;
  page: number;
  size: number;
}

export interface CreateConversationResponse {
  id: string;
  title: string;
  created_at: string;
  updated_at: string;
}

export interface StoredMessage {
  id: number;
  role: string;
  content: string;
  tokens_used: number;
  created_at: string;
}

export interface MessagesResponse {
  messages: StoredMessage[];
  total: number;
}

export async function listConversations(
  token: string,
  page = 1,
  size = 50
): Promise<ApiResponse<ConversationListResponse>> {
  return request<ConversationListResponse>(
    `/conversations?page=${page}&size=${size}`,
    { headers: authHeaders(token) }
  );
}

export async function createConversation(
  token: string,
  config?: Record<string, unknown>
): Promise<ApiResponse<CreateConversationResponse>> {
  return request<CreateConversationResponse>("/conversations", {
    method: "POST",
    headers: authHeaders(token),
    body: JSON.stringify(config ? { config } : {}),
  });
}

export async function deleteConversation(
  token: string,
  id: string
): Promise<ApiResponse<void>> {
  return request<void>(`/conversations/${id}`, {
    method: "DELETE",
    headers: authHeaders(token),
  });
}

export async function renameConversation(
  token: string,
  id: string,
  title: string
): Promise<ApiResponse<{ message: string }>> {
  return request<{ message: string }>(`/conversations/${id}`, {
    method: "PATCH",
    headers: authHeaders(token),
    body: JSON.stringify({ title }),
  });
}

export async function getConversationMessages(
  token: string,
  id: string,
  limit = 200
): Promise<ApiResponse<MessagesResponse>> {
  return request<MessagesResponse>(
    `/conversations/${id}/messages?limit=${limit}`,
    { headers: authHeaders(token) }
  );
}
