import type {
  ApiErrorBody,
  LoggingSettings,
  LoginResponse,
  SecuritySettings,
  Session,
} from "./types";
import { tl } from "./lib/i18n";

const CSRF_KEY = "vocat.csrf";

function isMutation(method: string) {
  return !["GET", "HEAD", "OPTIONS"].includes(method.toUpperCase());
}

function camelizeKey(key: string) {
  return key.replace(/_([a-z0-9])/g, (_, char: string) => char.toUpperCase());
}

function snakeizeKey(key: string) {
  return key
    .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
    .replace(/-/g, "_")
    .toLowerCase();
}

export function camelize<T>(value: unknown): T {
  if (Array.isArray(value)) return value.map((item) => camelize(item)) as T;
  if (value !== null && typeof value === "object") {
    return Object.fromEntries(
      Object.entries(value as Record<string, unknown>).map(([key, item]) => [
        camelizeKey(key),
        camelize(item),
      ]),
    ) as T;
  }
  return value as T;
}

function snakeize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map((item) => snakeize(item));
  if (value !== null && typeof value === "object") {
    return Object.fromEntries(
      Object.entries(value as Record<string, unknown>).map(([key, item]) => [
        snakeizeKey(key),
        snakeize(item),
      ]),
    );
  }
  return value;
}

export class ApiError extends Error {
  status: number;
  code: string;
  requestId: string;
  detail: ApiErrorBody;

  constructor(status: number, detail: ApiErrorBody) {
    super(detail.message || detail.error || `${tl("请求失败")}（HTTP ${status}）`);
    this.name = "ApiError";
    this.status = status;
    this.code = detail.code || "";
    this.requestId = detail.requestId || "";
    this.detail = detail;
  }
}

export interface RequestOptions extends Omit<RequestInit, "body"> {
  body?: unknown;
  raw?: boolean;
}

export async function api<T>(path: string, options: RequestOptions = {}): Promise<T> {
  const method = (options.method || "GET").toUpperCase();
  const headers = new Headers(options.headers);
  headers.set("Accept", options.raw ? "*/*" : "application/json");
  if (options.body !== undefined) headers.set("Content-Type", "application/json");
  if (isMutation(method)) {
    const csrf = sessionStorage.getItem(CSRF_KEY);
    if (csrf) headers.set("X-CSRF-Token", csrf);
  }

  const response = await fetch(path.startsWith("/api") ? path : `/api${path}`, {
    ...options,
    method,
    headers,
    credentials: "include",
    body: options.body === undefined ? undefined : JSON.stringify(snakeize(options.body)),
  });

  if (options.raw) return response as T;
  const contentType = response.headers.get("content-type") || "";
  const payload = contentType.includes("application/json")
    ? await response.json()
    : { message: await response.text() };
  const normalized = camelize<Record<string, unknown>>(payload);
  if (!response.ok) {
    if (response.status === 401) window.dispatchEvent(new Event("vocat:unauthorized"));
    const nested = normalized.error;
    const detail = nested && typeof nested === "object"
      ? {
          ...(nested as ApiErrorBody),
          requestId: (normalized.requestId as string | undefined) || (nested as ApiErrorBody).requestId,
        }
      : normalized as ApiErrorBody;
    throw new ApiError(response.status, detail);
  }
  return (Object.prototype.hasOwnProperty.call(normalized, "data") ? normalized.data : normalized) as T;
}

export async function login(username: string, password: string) {
  const result = await api<LoginResponse & { user?: { username?: string } }>("/auth/login", {
    method: "POST",
    body: { username, password },
  });
  if (result.csrfToken) sessionStorage.setItem(CSRF_KEY, result.csrfToken);
  return result;
}

export async function session() {
  const result = await api<Session & { user?: { username?: string } }>("/auth/session");
  if (result.csrfToken) sessionStorage.setItem(CSRF_KEY, result.csrfToken);
  return {
    ...result,
    username: result.username || result.user?.username || "",
    role: result.role || "Administrator",
  };
}

export async function logout() {
  try {
    await api("/auth/logout", { method: "POST" });
  } finally {
    sessionStorage.removeItem(CSRF_KEY);
  }
}

export function getSecuritySettings() {
  return api<SecuritySettings>("/settings/security");
}

export function updateSecuritySettings(settings: {
  mode: SecuritySettings["mode"];
  allowedCidrs: string[];
  trustProxyHeaders: boolean;
}) {
  return api<SecuritySettings>("/settings/security", { method: "PUT", body: settings });
}

export function getLoggingSettings() {
  return api<LoggingSettings>("/settings/logging");
}

export function updateLoggingSettings(settings: {
  mode: LoggingSettings["mode"];
  count: number;
  days: number;
}) {
  return api<LoggingSettings>("/settings/logging", { method: "PUT", body: settings });
}

export function apiMessage(error: unknown) {
  if (error instanceof ApiError) {
    const suffix = error.requestId ? `（${tl("请求")} ${error.requestId}）` : "";
    return `${error.message}${suffix}`;
  }
  if (error instanceof Error) return error.message;
  return tl("请求未完成，检查服务状态后重试");
}

export function eventStreamURL(path: string, params?: URLSearchParams) {
  const suffix = params?.toString();
  return `${path.startsWith("/api") ? path : `/api${path}`}${suffix ? `?${suffix}` : ""}`;
}
