import fs from "node:fs/promises";
import path from "node:path";
import type { Plugin, ResolvedConfig } from "vite";
import type { RecipesIndex } from "@ben-to/recipes/catalog";
import {
  buildRecipeOgHeadModel,
  forEachRecipeSharePair,
  injectRecipeSeoIntoIndexHtml
} from "@ben-to/recipes/recipe-og-meta";

const DEFAULT_SITE_ORIGIN = "https://ben-to.fr";

/**
 * Pré-rendu des pages recette pour les métadonnées Open Graph / Twitter (HTML initial).
 * - **Dev** : middleware qui réinjecte le `<head>` pour les URLs `/r/:base/:variant`.
 * - **Build** : écrit `dist/r/<base>/<variant>/index.html` pour chaque couple catalogue.
 */
export function recipeSeoPlugin(): Plugin {
  let config: ResolvedConfig;

  return {
    name: "ben-to-recipe-seo",
    configResolved(resolved) {
      config = resolved;
    },
    configureServer(server) {
      const root = server.config.root;
      const port = server.config.server.port ?? 3000;
      server.middlewares.use(async (req, res, next) => {
        if (req.method !== "GET" || !req.url) {
          next();
          return;
        }
        const pathname = new URL(req.url, "http://localhost").pathname.replace(/\/$/, "");
        const match = /^\/r\/([^/]+)\/([^/]+)$/.exec(pathname);
        if (!match) {
          next();
          return;
        }
        try {
          const baseId = decodeURIComponent(match[1]);
          const variantId = decodeURIComponent(match[2]);
          const catalogPath = path.join(root, "public/recipes/catalog.json");
          const indexPath = path.join(root, "index.html");
          const [catalogRaw, rawIndexHtml] = await Promise.all([
            fs.readFile(catalogPath, "utf8"),
            fs.readFile(indexPath, "utf8")
          ]);
          // On passe par le pipeline de transformation HTML de Vite (injecte la CSP
          // et tout autre plugin qui utilise `transformIndexHtml`) avant d'injecter
          // les balises OG/Twitter recette.
          const indexHtml = await server.transformIndexHtml(req.url ?? pathname, rawIndexHtml);
          const catalog = JSON.parse(catalogRaw) as RecipesIndex;
          const base = catalog.bases.find((b) => b.id === baseId);
          const variant = catalog.variants.find((v) => v.id === variantId);
          if (!base || !variant || !variant.baseIds.includes(baseId)) {
            next();
            return;
          }
          const devOrigin = process.env.BEN_TO_SITE_ORIGIN ?? `http://localhost:${port}`;
          const model = buildRecipeOgHeadModel({
            siteOrigin: devOrigin,
            base,
            variant,
            baseId,
            variantId
          });
          const html = injectRecipeSeoIntoIndexHtml(indexHtml, model);
          res.statusCode = 200;
          res.setHeader("Content-Type", "text/html; charset=utf-8");
          res.end(html);
        } catch {
          next();
        }
      });
    },
    async closeBundle() {
      const outDir = path.resolve(config.root, config.build.outDir);
      const indexPath = path.join(outDir, "index.html");
      const catalogPath = path.join(outDir, "recipes/catalog.json");
      const siteOrigin = process.env.BEN_TO_SITE_ORIGIN ?? DEFAULT_SITE_ORIGIN;
      const [indexHtml, catalogRaw] = await Promise.all([
        fs.readFile(indexPath, "utf8"),
        fs.readFile(catalogPath, "utf8")
      ]);
      const catalog = JSON.parse(catalogRaw) as RecipesIndex;
      const tasks: Promise<void>[] = [];
      forEachRecipeSharePair(catalog, ({ base, variant, baseId, variantId }) => {
        tasks.push(
          (async () => {
            const model = buildRecipeOgHeadModel({
              siteOrigin,
              base,
              variant,
              baseId,
              variantId
            });
            const html = injectRecipeSeoIntoIndexHtml(indexHtml, model);
            const dir = path.join(outDir, "r", encodeURIComponent(baseId), encodeURIComponent(variantId));
            await fs.mkdir(dir, { recursive: true });
            await fs.writeFile(path.join(dir, "index.html"), html, "utf8");
          })()
        );
      });
      await Promise.all(tasks);
      config.logger.info(`recipe-seo: ${tasks.length} page(s) OG générée(s) sous dist/r/`);
    }
  };
}
