import {
  getArticleBySlug,
  articlesFallback,
  type BlogArticle,
} from "@/data/articles";
import {
  getPortfolioBySlug,
  portfoliosFallback,
  type PortfolioItem,
} from "@/data/portfolios";

export const MESHKEE_DOMAIN = "havaran.com";
export const MESHKEE_API_BASE = "https://api.meshkee.com/api/v1";
export const MESHKEE_STORAGE_BASE =
  "https://meshkee-storage.sas.amin.parminstorage.ir";

type MeshkeeListResponse = {
  items?: unknown[];
  total?: number;
  page?: number;
  pageSize?: number;
};

export type ListResult<T> = {
  items: T[];
  total: number;
  page: number;
  pageSize: number;
  source: "api" | "fallback";
};

export const DEFAULT_LIST_PAGE_SIZE = 12;

type MeshkeePortfolioResponse = {
  portfolio?: unknown;
};

type MeshkeeBlogResponse = {
  blog?: unknown;
};

function asRecord(value: unknown): Record<string, unknown> | null {
  return value && typeof value === "object" && !Array.isArray(value)
    ? (value as Record<string, unknown>)
    : null;
}

function asString(value: unknown): string {
  return typeof value === "string" ? value.trim() : "";
}

function pickString(obj: Record<string, unknown>, keys: string[]): string {
  for (const key of keys) {
    const value = asString(obj[key]);
    if (value) return value;
  }
  return "";
}

/** Resolve any Meshkee / legacy CDN / relative media path to an absolute backend URL. */
export function resolveMediaUrl(value: string): string {
  const raw = value.trim();
  if (!raw) return "";

  let url = raw
    .replace(
      /^https?:\/\/cdn\.willaspace\.com\/uploads\//i,
      `${MESHKEE_STORAGE_BASE}/`,
    )
    .replace(
      /^https?:\/\/cdn\.meshkee\.com\/uploads\//i,
      `${MESHKEE_STORAGE_BASE}/`,
    )
    .replace(
      /^https?:\/\/cdn\.meshkee\.com\/meshkee-storage\//i,
      `${MESHKEE_STORAGE_BASE}/`,
    );

  if (url.startsWith("//")) {
    url = `https:${url}`;
  }

  if (url.startsWith("/")) {
    // Common API shapes: /uploads/... or /media/...
    const path = url.replace(/^\/+/, "");
    if (
      path.startsWith("uploads/") ||
      path.startsWith("media/") ||
      path.startsWith("rasam_") ||
      path.includes("/")
    ) {
      return `${MESHKEE_STORAGE_BASE}/${path.replace(/^uploads\//, "")}`;
    }
  }

  if (!/^https?:\/\//i.test(url) && url.includes("/")) {
    return `${MESHKEE_STORAGE_BASE}/${url.replace(/^\/+/, "")}`;
  }

  return url;
}

function mediaUrl(value: unknown): string {
  if (typeof value === "string") return resolveMediaUrl(value);
  const obj = asRecord(value);
  if (!obj) return "";
  const picked = pickString(obj, [
    "url",
    "src",
    "path",
    "thumbnail",
    "thumbnailUrl",
    "original",
    "originalUrl",
    "fullUrl",
    "fileUrl",
  ]);
  return resolveMediaUrl(picked);
}

function collectImages(value: unknown): string[] {
  if (!value) return [];
  if (typeof value === "string") {
    const url = resolveMediaUrl(value);
    return url ? [url] : [];
  }
  if (Array.isArray(value)) {
    return value.map(mediaUrl).filter(Boolean);
  }
  const obj = asRecord(value);
  if (!obj) return [];
  const nested =
    obj.items || obj.images || obj.media || obj.files || obj.gallery;
  if (nested) return collectImages(nested);
  const single = mediaUrl(obj);
  return single ? [single] : [];
}

function rewriteHtmlMedia(html: string): string {
  if (!html) return "";
  return html.replace(
    /\b(src|href)=["']([^"']+)["']/gi,
    (full, attr: string, value: string) => {
      if (!/\.(png|jpe?g|gif|webp|svg|avif)(\?|$)/i.test(value) && !value.includes("meshkee") && !value.includes("willaspace") && !value.includes("uploads/")) {
        // Keep non-media links as-is unless they look like storage paths.
        if (!value.includes("conversions/") && !value.startsWith("/uploads/")) {
          return full;
        }
      }
      const resolved = resolveMediaUrl(value);
      return resolved ? `${attr}="${resolved}"` : full;
    },
  );
}

function normalizePortfolio(raw: unknown): PortfolioItem | null {
  const obj = asRecord(raw);
  if (!obj) return null;

  const id = pickString(obj, ["id", "portfolioId", "_id"]) || "";
  const slug =
    pickString(obj, ["slug", "seoSlug", "permalink"]) || id || "";
  if (!id && !slug) return null;

  const title = pickString(obj, [
    "title",
    "titleFa",
    "name",
    "nameFa",
    "faTitle",
  ]);
  const titleEn = pickString(obj, [
    "titleEn",
    "englishTitle",
    "nameEn",
    "enTitle",
    "subtitle",
  ]);

  const imageCandidates = [
    ...collectImages(obj.titleImageUrl),
    ...collectImages(obj.thumbnail),
    ...collectImages(obj.coverImage),
    ...collectImages(obj.cover),
    ...collectImages(obj.image),
    ...collectImages(obj.featuredImage),
    ...collectImages(obj.media),
    ...collectImages(obj.images),
    ...collectImages(obj.gallery),
  ].filter(Boolean);

  const gallery = Array.from(
    new Set([
      ...collectImages(obj.gallery),
      ...collectImages(obj.images),
      ...collectImages(obj.media),
      ...collectImages(obj.photos),
    ]),
  );

  const image = resolveMediaUrl(imageCandidates[0] || gallery[0] || "");
  const coverRaw =
    pickString(obj, ["titleImageUrl", "coverImage", "cover", "featuredImage"]) ||
    imageCandidates[0] ||
    image;
  const coverImage = resolveMediaUrl(coverRaw) || image;

  const description = pickString(obj, [
    "abstract",
    "mainTextHtml",
    "description",
    "body",
    "content",
    "summary",
    "excerpt",
    "text",
  ]);

  return {
    id: id || slug,
    slug,
    title: title || titleEn || slug,
    titleEn,
    image,
    coverImage,
    gallery: (gallery.length ? gallery : image ? [image] : []).map(
      resolveMediaUrl,
    ),
    description,
  };
}

async function meshkeeGet<T>(path: string): Promise<T | null> {
  try {
    const res = await fetch(`${MESHKEE_API_BASE}${path}`, {
      headers: { Accept: "application/json" },
      next: { revalidate: 300 },
    });
    if (!res.ok) return null;
    return (await res.json()) as T;
  } catch {
    return null;
  }
}

export async function listPortfolios(options?: {
  page?: number;
  pageSize?: number;
}): Promise<ListResult<PortfolioItem>> {
  const page = Math.max(1, options?.page ?? 1);
  const pageSize = Math.max(1, options?.pageSize ?? DEFAULT_LIST_PAGE_SIZE);
  const data = await meshkeeGet<MeshkeeListResponse>(
    `/tenants/${MESHKEE_DOMAIN}/portfolios?page=${page}&pageSize=${pageSize}`,
  );

  const items = (data?.items || [])
    .map(normalizePortfolio)
    .filter((item): item is PortfolioItem => Boolean(item));

  if (items.length > 0 || (data && typeof data.total === "number")) {
    return {
      items,
      total: data?.total ?? items.length,
      page: data?.page ?? page,
      pageSize: data?.pageSize ?? pageSize,
      source: "api",
    };
  }

  const all = portfoliosFallback.map(normalizeFallbackPortfolio);
  const start = (page - 1) * pageSize;
  return {
    items: all.slice(start, start + pageSize),
    total: all.length,
    page,
    pageSize,
    source: "fallback",
  };
}

function normalizeFallbackPortfolio(item: PortfolioItem): PortfolioItem {
  return {
    ...item,
    image: resolveMediaUrl(item.image),
    coverImage: resolveMediaUrl(item.coverImage || item.image),
    gallery: item.gallery.map(resolveMediaUrl).filter(Boolean),
  };
}

function normalizeFallbackBlog(item: BlogArticle): BlogArticle {
  return {
    ...item,
    image: resolveMediaUrl(item.image),
    contentHtml: rewriteHtmlMedia(item.contentHtml),
  };
}

export async function getPortfolio(
  slug: string,
): Promise<{ item: PortfolioItem | null; source: "api" | "fallback" }> {
  const data = await meshkeeGet<MeshkeePortfolioResponse | Record<string, unknown>>(
    `/tenants/${MESHKEE_DOMAIN}/portfolios/${encodeURIComponent(slug)}`,
  );

  const raw = asRecord(data)?.portfolio ?? data;
  const fromApi = normalizePortfolio(raw);
  if (fromApi && (fromApi.title || fromApi.image || fromApi.gallery.length)) {
    return { item: fromApi, source: "api" };
  }

  return {
    item: (() => {
      const fallback = getPortfolioBySlug(slug);
      return fallback ? normalizeFallbackPortfolio(fallback) : null;
    })(),
    source: "fallback",
  };
}

function normalizeBlog(raw: unknown): BlogArticle | null {
  const obj = asRecord(raw);
  if (!obj) return null;

  const id = pickString(obj, ["id", "blogId", "_id"]) || "";
  const slug =
    pickString(obj, ["slug", "seoSlug", "permalink"]) || id || "";
  if (!id && !slug) return null;

  const title = pickString(obj, [
    "title",
    "titleFa",
    "name",
    "nameFa",
    "faTitle",
  ]);

  const imageCandidates = [
    ...collectImages(obj.titleImageUrl),
    ...collectImages(obj.thumbnail),
    ...collectImages(obj.coverImage),
    ...collectImages(obj.cover),
    ...collectImages(obj.image),
    ...collectImages(obj.featuredImage),
    ...collectImages(obj.media),
    ...collectImages(obj.images),
  ].filter(Boolean);

  const contentHtml = rewriteHtmlMedia(
    pickString(obj, [
      "mainTextHtml",
      "contentHtml",
      "html",
      "bodyHtml",
      "content",
      "body",
      "description",
      "text",
    ]),
  );

  const excerpt = pickString(obj, [
    "abstract",
    "excerpt",
    "summary",
    "shortDescription",
    "description",
  ]);

  const date = pickString(obj, [
    "publishedAt",
    "date",
    "createdAt",
    "updatedAt",
    "publishDate",
  ]).slice(0, 10);

  const plain =
    excerpt ||
    contentHtml
      .replace(/<[^>]+>/g, " ")
      .replace(/\s+/g, " ")
      .trim();

  // Prefer dedicated title image; fall back to first image inside article HTML.
  let image = resolveMediaUrl(imageCandidates[0] || "");
  if (!image && contentHtml) {
    const match = contentHtml.match(/\bsrc=["']([^"']+)["']/i);
    if (match?.[1]) image = resolveMediaUrl(match[1]);
  }

  return {
    id: id || slug,
    slug,
    title: title || slug,
    excerpt: plain.slice(0, 280),
    image,
    date,
    contentHtml,
  };
}

export async function listBlogs(options?: {
  page?: number;
  pageSize?: number;
  type?: "news" | "article" | "blog";
}): Promise<ListResult<BlogArticle>> {
  const page = Math.max(1, options?.page ?? 1);
  const pageSize = Math.max(1, options?.pageSize ?? DEFAULT_LIST_PAGE_SIZE);
  // Havaran publishes under type=blog (not article).
  const type = options?.type ?? "blog";
  const data = await meshkeeGet<MeshkeeListResponse>(
    `/tenants/${MESHKEE_DOMAIN}/blogs?page=${page}&pageSize=${pageSize}&type=${encodeURIComponent(type)}`,
  );

  const items = (data?.items || [])
    .map(normalizeBlog)
    .filter((item): item is BlogArticle => Boolean(item));

  if (items.length > 0 || (data && typeof data.total === "number" && data.total > 0)) {
    return {
      items,
      total: data?.total ?? items.length,
      page: data?.page ?? page,
      pageSize: data?.pageSize ?? pageSize,
      source: "api",
    };
  }

  // Retry without type filter in case tenant uses a different blog type.
  const untyped = await meshkeeGet<MeshkeeListResponse>(
    `/tenants/${MESHKEE_DOMAIN}/blogs?page=${page}&pageSize=${pageSize}`,
  );
  const untypedItems = (untyped?.items || [])
    .map(normalizeBlog)
    .filter((item): item is BlogArticle => Boolean(item));
  if (
    untypedItems.length > 0 ||
    (untyped && typeof untyped.total === "number" && untyped.total > 0)
  ) {
    return {
      items: untypedItems,
      total: untyped?.total ?? untypedItems.length,
      page: untyped?.page ?? page,
      pageSize: untyped?.pageSize ?? pageSize,
      source: "api",
    };
  }

  const all = articlesFallback.map(normalizeFallbackBlog);
  const start = (page - 1) * pageSize;
  return {
    items: all.slice(start, start + pageSize),
    total: all.length,
    page,
    pageSize,
    source: "fallback",
  };
}

export async function getBlog(
  slug: string,
): Promise<{ item: BlogArticle | null; source: "api" | "fallback" }> {
  const data = await meshkeeGet<MeshkeeBlogResponse | Record<string, unknown>>(
    `/tenants/${MESHKEE_DOMAIN}/blogs/${encodeURIComponent(slug)}`,
  );

  const raw = asRecord(data)?.blog ?? data;
  const fromApi = normalizeBlog(raw);
  if (fromApi && (fromApi.title || fromApi.contentHtml || fromApi.excerpt)) {
    return { item: fromApi, source: "api" };
  }

  const fallback = getArticleBySlug(slug);
  return {
    item: fallback ? normalizeFallbackBlog(fallback) : null,
    source: "fallback",
  };
}
