import type { Request, Response, NextFunction } from 'express';
import { CategoryService } from '../services/category.service';
import { buildCategoryTree, getAllDescendantIds, findCategoryById, type Category } from '../models/category.model';
import { ProductService } from '../services/product.service';
import { type Product, toProduct } from '../models/product.model';
import { pLimit } from '../utils/concurrency';
import { cache }  from '../services/cache.service';

export const CategoryController = {
  async getTree(_req: Request, res: Response, next: NextFunction) {
    try {
      const cached = cache.get<Category[]>('categories:tree');
      if (cached) return res.json(cached);

      const flat = await CategoryService.getAll();
      const counts = await pLimit(
        flat.map((c) => async () => {
          const products = await CategoryService.getProductsByCategory(c.id);
          return [Number(c.id), products.length] as [number, number];
        }),
        2
      );

      const countMap = new Map(counts);
      const fullTree = buildCategoryTree(flat, countMap);

      // Si une seule racine "conteneur" (ex: PdV) existe, on la déballe
      // pour que ses enfants deviennent les vraies catégories principales
      const tree = fullTree.length === 1 ? fullTree[0].children : fullTree;

      cache.set('categories:tree', tree, 600);
      res.json(tree);
    } catch (err) {
      next(err);
    }
  },

  async getProducts(req: Request, res: Response, next: NextFunction) {
    try {
      const categoryId = Number(req.params.id);
      const cacheKey = `categories:${categoryId}:products`;

      const cached = cache.get<Product[]>(cacheKey);
      if (cached) return res.json(cached);

      // Récupère l'arbre complet pour trouver tous les descendants de la catégorie cliquée
      let tree = cache.get<Category[]>('categories:tree');
      if (!tree) {
        const flat = await CategoryService.getAll();
        const fullTree = buildCategoryTree(flat, new Map());
        tree = fullTree.length === 1 ? fullTree[0].children : fullTree;
      }

      const targetCat = findCategoryById(tree, categoryId);
      const idsToFetch = targetCat ? getAllDescendantIds(targetCat) : [categoryId];

      // Récupère les produits de la catégorie ET de toutes ses sous-catégories
      const refsPerCategory = await pLimit(
        idsToFetch.map((id) => () => CategoryService.getProductsByCategory(id)),
        10
      );

      // Dédoublonne les IDs produits (au cas où un produit serait lié à plusieurs catégories)
      const uniqueProductIds = Array.from(
        new Set(refsPerCategory.flat().map((r) => r.id))
      );

      const products = await pLimit(
        uniqueProductIds.map((id) => () => ProductService.getById(id).then(toProduct)),
        10
      );

      cache.set(cacheKey, products, 600);
      res.json(products);
    } catch (err) {
      next(err);
    }
  },
};

