import type {
  HelpCategory,
  CreateHelpCategoryPayload,
  UpdateHelpCategoryPayload
} from "@/domain/entities/help-category";
import type {
  HelpCategoriesPaginatorMeta,
  HelpCategoriesResponse,
  HelpCategoryFindAllOptions,
  HelpCategoryRepository
} from "@/domain/repositories/help-category.repository";
import { apiClient } from "@/infrastructure/api/api-client";

function isPlainObject(v: unknown): v is Record<string, unknown> {
  return typeof v === "object" && v !== null && !Array.isArray(v);
}

function pickNumber(...candidates: unknown[]): number | undefined {
  for (const c of candidates) {
    if (typeof c === "number" && Number.isFinite(c)) return c;
    if (typeof c === "string" && c !== "" && !Number.isNaN(Number(c))) return Number(c);
  }
  return undefined;
}

/**
 * Backend `HelpCategoryCollection::toArray` returns:
 *   {
 *     total, count, per_page,
 *     currentPage, lastPage, totalPages,
 *     previousPageUrl, nextPageUrl,
 *     help_categories: [ ... ]
 *   }
 * wrapped in the `successHttpResponse` envelope `{ status, message, data: <above> }`.
 *
 * This normalizer also tolerates a few other common shapes (plain array,
 * Laravel default `{ data, meta, links }`, project `BackendPaginatedData`) so
 * the frontend keeps working if the backend response evolves.
 */
function normalize(raw: unknown): HelpCategoriesResponse {
  // Unwrap optional ApiResponse envelope: { status, message, data: <payload> }
  let body: unknown = raw;
  if (
    isPlainObject(body) &&
    "data" in body &&
    body.data !== undefined &&
    ("status" in body || "message" in body)
  ) {
    body = body.data;
  }

  if (Array.isArray(body)) {
    return { data: body as HelpCategory[] };
  }

  if (!isPlainObject(body)) {
    return { data: [] };
  }

  const items =
    (Array.isArray(body.help_categories) ? (body.help_categories as HelpCategory[]) : undefined) ??
    (Array.isArray(body.data) ? (body.data as HelpCategory[]) : undefined) ??
    (Array.isArray(body.items) ? (body.items as HelpCategory[]) : undefined) ??
    [];

  const rawMeta = isPlainObject(body.meta) ? body.meta : undefined;
  const innerMeta = isPlainObject(rawMeta?.pagination) ? rawMeta!.pagination : undefined;

  const current_page = pickNumber(
    body.currentPage,
    body.current_page,
    rawMeta?.current_page,
    innerMeta?.current_page,
  );
  const last_page = pickNumber(
    body.lastPage,
    body.last_page,
    body.totalPages,
    body.total_pages,
    rawMeta?.last_page,
    innerMeta?.last_page,
    rawMeta?.total_pages,
  );
  const per_page = pickNumber(body.per_page, body.perPage, rawMeta?.per_page, innerMeta?.per_page);
  const total = pickNumber(body.total, body.count, rawMeta?.total, innerMeta?.total);

  const hasAnyMeta =
    current_page !== undefined ||
    last_page !== undefined ||
    per_page !== undefined ||
    total !== undefined;

  if (!hasAnyMeta) {
    return { data: items };
  }

  const meta: HelpCategoriesPaginatorMeta = {
    current_page: current_page ?? 1,
    last_page: last_page ?? 1,
    per_page: per_page ?? items.length,
    total: total ?? items.length,
  };

  const links: Record<string, string | null> | undefined =
    typeof body.previousPageUrl !== "undefined" || typeof body.nextPageUrl !== "undefined"
      ? {
          prev: (body.previousPageUrl as string | null | undefined) ?? null,
          next: (body.nextPageUrl as string | null | undefined) ?? null,
        }
      : isPlainObject(body.links)
        ? (body.links as Record<string, string | null>)
        : undefined;

  return { data: items, meta, links };
}

export class HelpCategoryRepositoryImpl implements HelpCategoryRepository {
  async findAll(options?: HelpCategoryFindAllOptions): Promise<HelpCategoriesResponse> {
    const params: Record<string, string | number> = {};

    if (options?.all) {
      params.per_page = "all";
    } else if (options) {
      if (options.page) params.page = options.page;
      if (options.per_page) params.per_page = options.per_page;
      if (options.search) params.search = options.search;

      const sortBy = options.sort_by ?? options.order_by;
      const sortDir =
        options.sort_dir ?? (options.order ? (options.order.toLowerCase() as "asc" | "desc") : undefined);
      if (sortBy) params.sort_by = sortBy;
      if (sortDir) params.sort_dir = sortDir;
    }

    const response = await apiClient.get("/help-categories", { params });
    return normalize(response.data);
  }

  async findById(id: string | number): Promise<HelpCategory> {
    const response = await apiClient.get(`/help-categories/${id}`);
    return response.data?.data || response.data;
  }

  async create(payload: CreateHelpCategoryPayload): Promise<HelpCategory> {
    const response = await apiClient.post("/help-categories", payload);
    return response.data?.data || response.data;
  }

  async update(id: string | number, payload: UpdateHelpCategoryPayload): Promise<HelpCategory> {
    const response = await apiClient.put(`/help-categories/${id}`, payload);
    return response.data?.data || response.data;
  }

  async delete(id: string | number): Promise<void> {
    await apiClient.delete(`/help-categories/${id}`);
  }

  async toggleVisibility(id: string | number): Promise<HelpCategory> {
    const response = await apiClient.patch(`/help-categories/${id}/toggle-visibility`);
    return response.data?.data || response.data;
  }
}
