import rawProducts from "./kidz-products.json";
import rawBundles from "./kidz-bundles.json";

export const KIDZ_PRODUCT_LIBRARY_VERSION = "1.0";
export const KIDZ_PRODUCT_SCHEMA_VERSION = "1.0";

export type KidzCategory = "Explore" | "Learn" | "Play" | "Create" | "Junior Ranger" | "Holiday Activities";
export type KidzDifficulty = "Easy" | "Moderate" | "Challenging";
export type KidzDesignation = "Printable" | "Printable and digital" | "Digital";
export type KidzPriceClassification = "Free" | "Free sample" | "Premium-ready" | "Bundle-only" | "Personalised premium-ready";
export type KidzDownloadStatus = "available" | "sample-available" | "preview" | "bundle-preview" | "request-personalisation";

export type KidzFaq = { question: string; answer: string };
export type KidzProduct = {
  id: string;
  number: number;
  slug: string;
  title: string;
  shortTitle: string;
  description: string;
  longCopy: string[];
  category: KidzCategory;
  productType: string;
  ageRange: { min: number; max: number; label: string };
  schoolLevel: string;
  difficulty: KidzDifficulty;
  duration: string;
  designation: KidzDesignation;
  pageCount: number;
  fileFormats: string[];
  fileSize: string;
  coverImage: string;
  previewImages: string[];
  learningOutcomes: string[];
  skillsDeveloped: string[];
  materialsRequired: string[];
  parentGuidance: string;
  wildlifeSafetyNote: string;
  conservationNote: string;
  downloadStatus: KidzDownloadStatus;
  priceClassification: KidzPriceClassification;
  bundleEligibility: boolean;
  bundleSlugs: string[];
  featured: boolean;
  publicFile: string | null;
  sampleFile: string | null;
  internalMaster: string;
  seoTitle: string;
  seoDescription: string;
  keywords: string[];
  openGraphImage: string;
  schemaType: "CreativeWork";
  version: string;
  lastUpdated: string;
  includes: string[];
  faqs: KidzFaq[];
  printSpecs: {
    primarySize: "A4";
    letterVariant: boolean;
    margins: string;
    duplex: string;
    colourMode: string;
    inkFriendly: boolean;
  };
  accessibility: {
    readingOrderReviewed: boolean;
    colourIndependentInstructions: boolean;
    minimumBodyText: string;
    plainLanguage: boolean;
  };
};

export type KidzBundle = {
  id: string;
  slug: string;
  title: string;
  description: string;
  productSlugs: string[];
  priceClassification: "Premium-ready" | "Bundle-only";
  status: "preview";
  coverImage: string;
  featured: boolean;
  version: string;
  lastUpdated: string;
  seoTitle: string;
  seoDescription: string;
};

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

function requireString(record: Record<string, unknown>, key: string, context: string) {
  if (typeof record[key] !== "string" || !String(record[key]).trim()) throw new Error(`${context}: missing ${key}`);
}

function validateProducts(value: unknown): KidzProduct[] {
  if (!Array.isArray(value) || value.length !== 50) throw new Error(`MPC Kidz product data must contain exactly 50 products; received ${Array.isArray(value) ? value.length : "non-array"}`);
  const ids = new Set<string>();
  const slugs = new Set<string>();
  const products = value.map((item, index) => {
    if (!isObject(item)) throw new Error(`MPC Kidz product ${index + 1} is not an object`);
    const context = `MPC Kidz product ${index + 1}`;
    ["id", "slug", "title", "shortTitle", "description", "category", "productType", "difficulty", "duration", "designation", "downloadStatus", "priceClassification", "coverImage", "openGraphImage", "version", "lastUpdated"].forEach((key) => requireString(item, key, context));
    if (!Number.isInteger(item.number) || Number(item.number) < 1 || Number(item.number) > 50) throw new Error(`${context}: invalid number`);
    if (!Number.isInteger(item.pageCount) || Number(item.pageCount) < 3) throw new Error(`${context}: invalid pageCount`);
    if (!Array.isArray(item.previewImages) || item.previewImages.length < 2) throw new Error(`${context}: at least two preview images are required`);
    if (!Array.isArray(item.learningOutcomes) || item.learningOutcomes.length < 3) throw new Error(`${context}: learning outcomes are incomplete`);
    if (!Array.isArray(item.faqs) || item.faqs.length < 2) throw new Error(`${context}: FAQs are incomplete`);
    const id = String(item.id);
    const slug = String(item.slug);
    if (ids.has(id)) throw new Error(`${context}: duplicate id ${id}`);
    if (slugs.has(slug)) throw new Error(`${context}: duplicate slug ${slug}`);
    ids.add(id);
    slugs.add(slug);
    const tier = String(item.priceClassification) as KidzPriceClassification;
    if (tier === "Free" && typeof item.publicFile !== "string") throw new Error(`${context}: free product requires a publicFile`);
    if (tier !== "Free" && item.publicFile) throw new Error(`${context}: non-free full master must not be public`);
    if (tier === "Free sample" && typeof item.sampleFile !== "string") throw new Error(`${context}: free sample requires a sampleFile`);
    return item as unknown as KidzProduct;
  });
  return products.sort((a, b) => a.number - b.number);
}

function validateBundles(value: unknown, products: KidzProduct[]): KidzBundle[] {
  if (!Array.isArray(value) || value.length !== 5) throw new Error("MPC Kidz bundle data must contain exactly five bundles");
  const productSlugs = new Set(products.map((product) => product.slug));
  const bundleSlugs = new Set<string>();
  return value.map((item, index) => {
    if (!isObject(item)) throw new Error(`MPC Kidz bundle ${index + 1} is not an object`);
    const context = `MPC Kidz bundle ${index + 1}`;
    ["id", "slug", "title", "description", "coverImage", "version", "lastUpdated"].forEach((key) => requireString(item, key, context));
    const slug = String(item.slug);
    if (bundleSlugs.has(slug)) throw new Error(`${context}: duplicate slug ${slug}`);
    bundleSlugs.add(slug);
    if (!Array.isArray(item.productSlugs) || item.productSlugs.length < 5) throw new Error(`${context}: bundle needs at least five products`);
    for (const productSlug of item.productSlugs) {
      if (!productSlugs.has(String(productSlug))) throw new Error(`${context}: unknown product ${productSlug}`);
    }
    return item as unknown as KidzBundle;
  });
}

export const kidzProducts = validateProducts(rawProducts);
export const kidzBundles = validateBundles(rawBundles, kidzProducts);

export const kidzCategories = [...new Set(kidzProducts.map((product) => product.category))] as KidzCategory[];
export const kidzProductTypes = [...new Set(kidzProducts.map((product) => product.productType))].sort((a, b) => a.localeCompare(b));
export const kidzDifficulties: KidzDifficulty[] = ["Easy", "Moderate", "Challenging"];

export function getKidzProduct(slug: string) {
  return kidzProducts.find((product) => product.slug === slug);
}

export function getKidzBundle(slug: string) {
  return kidzBundles.find((bundle) => bundle.slug === slug);
}

export function productsForBundle(bundle: KidzBundle) {
  return bundle.productSlugs.map((slug) => getKidzProduct(slug)).filter((product): product is KidzProduct => Boolean(product));
}

export function relatedKidzProducts(product: KidzProduct, limit = 4) {
  const sameBundle = new Set(product.bundleSlugs.flatMap((bundleSlug) => getKidzBundle(bundleSlug)?.productSlugs || []));
  return kidzProducts
    .filter((candidate) => candidate.slug !== product.slug)
    .sort((a, b) => {
      const aScore = (a.category === product.category ? 3 : 0) + (sameBundle.has(a.slug) ? 2 : 0) + (a.featured ? 1 : 0);
      const bScore = (b.category === product.category ? 3 : 0) + (sameBundle.has(b.slug) ? 2 : 0) + (b.featured ? 1 : 0);
      return bScore - aScore || a.number - b.number;
    })
    .slice(0, limit);
}

export function publicKidzFile(product: KidzProduct) {
  return product.publicFile || product.sampleFile;
}
