import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
import { dirname, relative, resolve } from "node:path";
import { defineConfig } from "vite";
import { orderlySsrDevServer } from "@orderlyshop/web-components/server/vite";

export default defineConfig({
  server: {
    host: "localhost",
    port: 61677,
    strictPort: true
  },
  preview: {
    host: "localhost",
    port: 61677,
    strictPort: true
  },
  resolve: {
    alias: sourceAliases()
  },
  plugins: [htmlIncludes(), orderlySsrDevServer(), rootHtmlOutput()],
  build: {
    rollupOptions: {
      input: htmlInputs()
    }
  }
});

function sourceAliases() {
  const webComponentsSource = resolve(process.cwd(), "../../src/index.ts");
  const coreClientSource = resolve(process.cwd(), "../../../core-client/src/index.ts");
  const coreClientWebSource = resolve(process.cwd(), "../../../core-client/src/web.ts");
  if (!existsSync(webComponentsSource)) {
    return {};
  }
  return [
    { find: /^@orderlyshop\/core-client\/web$/, replacement: coreClientWebSource },
    { find: /^@orderlyshop\/core-client$/, replacement: coreClientSource },
    { find: "@orderlyshop/web-components", replacement: webComponentsSource }
  ];
}

function htmlIncludes() {
  return {
    name: "orderly-html-includes",
    transformIndexHtml: {
      order: "pre",
      handler(html) {
        return html
          .replace("<!-- orderly-head-includes -->", include("head.html"))
          .replace("<!-- orderly-body-start-includes -->", include("body-start.html"))
          .replace("<!-- orderly-body-end-includes -->", include("body-end.html"));
      }
    }
  };
}

function rootHtmlOutput() {
  return {
    name: "orderly-root-html-output",
    enforce: "post",
    generateBundle(_, bundle) {
      for (const [fileName, output] of Object.entries(bundle)) {
        if (!fileName.startsWith("src/") || !fileName.endsWith(".html")) {
          continue;
        }
        delete bundle[fileName];
        output.fileName = fileName.slice("src/".length);
        bundle[output.fileName] = output;
      }
    }
  };
}

function include(fileName) {
  return includeFile(resolve(process.cwd(), "src/includes", fileName));
}

function includeFile(filePath) {
  return expandTemplateIncludes(readFileSync(filePath, "utf8").trim(), dirname(filePath));
}

function expandTemplateIncludes(html, baseDir) {
  return html.replace(/<!--\s*orderly-include-template:\s*([^>]+?)\s*-->/g, (_, templatePath) => {
    return includeFile(resolve(baseDir, templatePath.trim()));
  });
}

function htmlInputs(root = process.cwd()) {
  const inputs = {};
  for (const file of listHtmlFiles(root)) {
    const name = htmlInputName(root, file);
    inputs[name] = file;
  }
  return inputs;
}

function htmlInputName(root, file) {
  const name = relative(root, file).replace(/\\/g, "/").replace(/\.html$/, "");
  return name.startsWith("src/") ? name.slice("src/".length) : name;
}

function listHtmlFiles(dir) {
  const files = [];
  for (const entry of readdirSync(dir)) {
    if (entry === "dist" || entry === "node_modules") {
      continue;
    }
    const fullPath = resolve(dir, entry);
    if (isHtmlPartial(fullPath)) {
      continue;
    }
    const stat = statSync(fullPath);
    if (stat.isDirectory()) {
      files.push(...listHtmlFiles(fullPath));
    } else if (entry.endsWith(".html")) {
      files.push(fullPath);
    }
  }
  return files;
}

function isHtmlPartial(file) {
  const path = relative(process.cwd(), file);
  return path.startsWith("src/includes") || path.startsWith("src/templates");
}
