import type {
  HelpArticle,
  HelpArticleBlockLibrary,
  HelpArticleStats,
  HelpCategoryOption,
  CreateHelpArticlePayload,
  UpdateHelpArticlePayload
} from "@/domain/entities/help-article";
import type {
  HelpArticlesPaginatorMeta,
  HelpArticlesResponse,
  HelpArticleBlocksOptions,
  HelpArticleFindAllOptions,
  HelpArticleRepository
} from "@/domain/repositories/help-article.repository";
import { apiClient } from "@/infrastructure/api/api-client";
import { env } from "@/infrastructure/config/env";

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;
}

/**
 * Same convention as help-categories / help-sections collections: backend
 * returns `{ status, message, data: { ..., help_articles: [...], currentPage, lastPage, ... } }`.
 * Accepts Laravel default `{ data, meta }` and project `BackendPaginatedData`
 * as fallbacks.
 */
function normalize(raw: unknown): HelpArticlesResponse {
  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 HelpArticle[] };
  }

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

  const items =
    (Array.isArray(body.help_articles) ? (body.help_articles as HelpArticle[]) : undefined) ??
    (Array.isArray(body.data) ? (body.data as HelpArticle[]) : undefined) ??
    (Array.isArray(body.items) ? (body.items as HelpArticle[]) : 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: HelpArticlesPaginatorMeta = {
    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 HelpArticleRepositoryImpl implements HelpArticleRepository {
  async findAll(options?: HelpArticleFindAllOptions): Promise<HelpArticlesResponse> {
    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-articles", { params });
    return normalize(response.data);
  }

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

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

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

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

  async getStats(): Promise<HelpArticleStats> {
    const response = await apiClient.get("/help-articles/stats");
    return response.data?.data || response.data;
  }

  async getCategoryOptions(): Promise<HelpCategoryOption[]> {
    const response = await apiClient.get("/api/v1/fleet/sidebar/options", { baseURL: env.backendOrigin });
    return response.data?.data || response.data;
  }

  async getBlocks(options?: HelpArticleBlocksOptions): Promise<HelpArticleBlockLibrary> {
    const params: Record<string, string | number> = {};
    if (options?.help_category_id) params.help_category_id = options.help_category_id;
    if (options?.tone) params.tone = options.tone;

    const response = await apiClient.get("/help-articles/blocks", { params });
    return response.data?.data || response.data;
  }
}
