import { apiBase, itemsOrEmpty } from "../api";
import type { Post } from "../types/post";

export type FavoriteSort = "favorited_at" | "published_at" | "hot";

export type FavoriteListResponse = {
  items: Post[];
  page?: number;
  limit?: number;
  total?: number;
};

export type FavoriteIdsResponse = {
  ids: string[];
};

export type FavoriteMutationResponse = {
  ok: boolean;
  changed?: boolean;
  resourceId?: string;
  favorited?: boolean;
  favoritedAt?: string;
  favoriteCount?: number;
};

function authHeaders(token: string): HeadersInit {
  return { Authorization: `Bearer ${token}` };
}

function authJSONHeaders(token: string): HeadersInit {
  return {
    ...authHeaders(token),
    "Content-Type": "application/json",
  };
}

/** HTTP error that preserves the status code so callers can react to 401s. */
export class FavoriteHttpError extends Error {
  readonly status: number;
  constructor(status: number, message: string) {
    super(message || `Request failed (${status})`);
    this.name = "FavoriteHttpError";
    this.status = status;
  }
}

/** True when an error means the wallet session is no longer authorized. */
export function isFavoritesAuthError(error: unknown): boolean {
  return error instanceof FavoriteHttpError && error.status === 401;
}

async function parseJSON<T>(res: Response): Promise<T> {
  if (!res.ok) throw new FavoriteHttpError(res.status, await res.text());
  return res.json() as Promise<T>;
}

export async function listFavorites(
  token: string,
  params: {
    sort?: FavoriteSort;
    page?: number;
    limit?: number;
    category?: string;
    q?: string;
    includeUnavailable?: boolean;
    lang?: string;
  } = {},
): Promise<FavoriteListResponse> {
  const sp = new URLSearchParams();
  Object.entries(params).forEach(([key, value]) => {
    if (value === undefined || value === "") return;
    sp.set(key, String(value));
  });
  const suffix = sp.toString() ? `?${sp}` : "";
  const res = await fetch(`${apiBase}/api/favorites${suffix}`, {
    headers: authHeaders(token),
  });
  return parseJSON<FavoriteListResponse>(res);
}

export async function getFavoriteIds(
  token: string,
  resourceIds: string[],
): Promise<string[]> {
  if (resourceIds.length === 0) return [];
  const uniqueIds = [...new Set(resourceIds)].slice(0, 100);
  const res = await fetch(
    `${apiBase}/api/favorites?ids=${encodeURIComponent(uniqueIds.join(","))}`,
    { headers: authHeaders(token) },
  );
  const data = await parseJSON<FavoriteIdsResponse | FavoriteListResponse>(res);
  if ("ids" in data && Array.isArray(data.ids)) return data.ids;
  if ("items" in data) return itemsOrEmpty(data.items).map((item) => item.id);
  return [];
}

export async function addFavorite(
  token: string,
  resourceId: string,
): Promise<FavoriteMutationResponse> {
  const res = await fetch(`${apiBase}/api/posts/${resourceId}/favorite`, {
    method: "POST",
    headers: authJSONHeaders(token),
    body: JSON.stringify({ add: true }),
  });
  return parseJSON<FavoriteMutationResponse>(res);
}

export async function removeFavorite(
  token: string,
  resourceId: string,
): Promise<FavoriteMutationResponse> {
  const res = await fetch(`${apiBase}/api/posts/${resourceId}/favorite`, {
    method: "POST",
    headers: authJSONHeaders(token),
    body: JSON.stringify({ add: false }),
  });
  return parseJSON<FavoriteMutationResponse>(res);
}
