import { mkdir, readFile, writeFile, copyFile } from "node:fs/promises";
import { existsSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";

const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const sourceDir = process.env.NODEDC_SOURCE_DIR || "/Users/dcconstructions/site-rescue/ssscript.app";
const targetDir = repoRoot;

const pages = ["index.html", "beta-apply.html", "founders.html", "enterprise.html"];

const assetHosts = new Set([
  "cdn.prod.website-files.com",
  "d3e54v103j8qbb.cloudfront.net",
  "ssscript-website.vercel.app",
  "ssscript.dev",
  "cloud.umami.is",
]);

const assetExtensions = new Set([
  ".avif",
  ".css",
  ".glb",
  ".gif",
  ".jpg",
  ".jpeg",
  ".js",
  ".mp4",
  ".otf",
  ".png",
  ".svg",
  ".ttf",
  ".webm",
  ".webp",
  ".woff",
  ".woff2",
]);

const assetMap = new Map();
const queue = [];
const failures = [];

function cleanUrl(raw) {
  let value = raw.trim();
  value = value.replace(/&amp;/g, "&");
  value = value.replace(/[),.;]+$/g, "");
  if (value.startsWith("//")) value = `https:${value}`;
  return value;
}

function extensionFor(url) {
  return path.extname(decodeURIComponent(url.pathname)).toLowerCase();
}

function isAssetUrl(url) {
  if (!assetHosts.has(url.hostname)) return false;
  if (url.href.includes("%7B") || url.href.includes("%7D") || url.href.includes("${")) return false;
  if (url.hostname === "cdn.prod.website-files.com" && url.pathname.includes("/vendor/webflow-badge-")) return false;
  if (url.hostname === "cloud.umami.is") return url.pathname === "/script.js";
  return assetExtensions.has(extensionFor(url));
}

function localAssetPath(url) {
  const decodedPath = decodeURIComponent(url.pathname);
  const basename = path.basename(decodedPath);
  const ext = path.extname(basename).toLowerCase();

  if (url.hostname === "cdn.prod.website-files.com") {
    if (decodedPath.includes("/css/")) return path.join("assets", "webflow", "css", basename);
    if (decodedPath.includes("/js/")) return path.join("assets", "webflow", "js", basename);
    return path.join("assets", "webflow", "images", basename);
  }

  if (url.hostname === "d3e54v103j8qbb.cloudfront.net") {
    return path.join("assets", "vendor", basename);
  }

  if (url.hostname === "ssscript-website.vercel.app") {
    if (ext === ".css") return path.join("assets", "custom", "css", basename);
    if (ext === ".js") return path.join("assets", "custom", "js", basename);
    return path.join("assets", "custom", basename);
  }

  if (url.hostname === "ssscript.dev") {
    return path.join("assets", "media", basename);
  }

  if (url.hostname === "cloud.umami.is") {
    return path.join("assets", "vendor", "umami-script.js");
  }

  return path.join("assets", url.hostname, basename);
}

function addAsset(rawUrl, baseUrl = undefined) {
  let url;
  try {
    url = new URL(cleanUrl(rawUrl), baseUrl);
  } catch {
    return null;
  }

  if (!isAssetUrl(url)) return null;

  const canonical = url.href;
  if (!assetMap.has(canonical)) {
    const localPath = localAssetPath(url);
    assetMap.set(canonical, localPath);
    queue.push({ url, localPath });
  }
  return assetMap.get(canonical);
}

function extractUrls(text) {
  const urls = [];
  const absoluteUrlPattern = /(?:https?:)?\/\/[^\s"'<>\\]+/g;
  const cssUrlPattern = /url\((['"]?)([^'")]+)\1\)/g;
  const relativeAssetPattern =
    /(["'`])((?:\.{1,2}\/|\/)[^"'`?#)]+\.(?:avif|css|gif|glb|jpe?g|js|mp4|otf|png|svg|ttf|webm|webp|woff2?)(?:[?#][^"'`]*)?)\1/g;

  for (const match of text.matchAll(absoluteUrlPattern)) {
    urls.push(match[0]);
  }
  for (const match of text.matchAll(cssUrlPattern)) {
    urls.push(match[2]);
  }
  for (const match of text.matchAll(relativeAssetPattern)) {
    urls.push(match[2]);
  }

  return urls;
}

function rel(fromFile, toFile) {
  const relative = path.relative(path.dirname(fromFile), toFile).replaceAll(path.sep, "/");
  const webPath = relative.split("/").map(encodeURIComponent).join("/");
  return webPath.startsWith(".") ? webPath : `./${webPath}`;
}

function pageRel(localPath) {
  return `./${localPath.split(path.sep).join("/").split("/").map(encodeURIComponent).join("/")}`;
}

function rewriteUrls(text, fromLocalPath, baseUrl = undefined) {
  let output = text;
  const isJs = fromLocalPath.endsWith(".js");

  const replacements = [...assetMap.entries()].map(([remote, local]) => {
    const remoteUrl = new URL(remote);
    return {
      remote,
      local,
      relative: isJs ? pageRel(local) : rel(path.join(targetDir, fromLocalPath), path.join(targetDir, local)),
      noQuery: `${remoteUrl.origin}${remoteUrl.pathname}`,
    };
  });

  for (const item of replacements) {
    output = output.split(item.remote).join(item.relative);
    output = output.split(item.remote.replaceAll("&", "&amp;")).join(item.relative);
    output = output.split(item.noQuery).join(item.relative);
    output = output.split(item.noQuery.replaceAll("&", "&amp;")).join(item.relative);
  }

  if (baseUrl) {
    output = output.replace(/url\((['"]?)([^'")]+)\1\)/g, (full, quote, value) => {
      const local = addAsset(value, baseUrl);
      if (!local) return full;
      const localRef = isJs ? pageRel(local) : rel(path.join(targetDir, fromLocalPath), path.join(targetDir, local));
      return `url(${quote}${localRef}${quote})`;
    });

    output = output.replace(
      /(["'`])((?:\.{1,2}\/|\/)[^"'`?#)]+\.(?:avif|css|gif|glb|jpe?g|js|mp4|otf|png|svg|ttf|webm|webp|woff2?)(?:[?#][^"'`]*)?)\1/g,
      (full, quote, value) => {
        const local = addAsset(value, baseUrl);
        if (!local) return full;
        const localRef = isJs ? pageRel(local) : rel(path.join(targetDir, fromLocalPath), path.join(targetDir, local));
        return `${quote}${localRef}${quote}`;
      }
    );
  }

  if (fromLocalPath.endsWith(".html")) {
    output = output
      .replace(/<link href="https:\/\/cdn\.prod\.website-files\.com" rel="preconnect"[^>]*>/g, "")
      .replace(/<link rel="preconnect" href="https:\/\/ssscript\.app\/cdn\.prod\.website-files\.com"[^>]*>/g, "")
      .replaceAll("https://localhost:6545/app.js", "./assets/custom/js/app.js");

    if (!output.includes("window.__API_ORIGIN__")) {
      output = output.replace(
        "<script>\n(function(d,h,host){",
        '<script>\nwindow.__API_ORIGIN__="https://ssscript-website.vercel.app";\n(function(d,h,host){'
      );
    }
  }

  output = output
    .replace(/\s+integrity="[^"]*"/g, "")
    .replace(/\s+crossorigin="anonymous"/g, "")
    .replace(/\s+crossOrigin="anonymous"/g, "");

  return output;
}

async function downloadAsset(item) {
  const outPath = path.join(targetDir, item.localPath);
  await mkdir(path.dirname(outPath), { recursive: true });

  const ext = extensionFor(item.url);
  if (existsSync(outPath) && ext !== ".css" && ext !== ".js") return;

  const response = await fetch(item.url.href, {
    redirect: "follow",
    headers: {
      "User-Agent": "Mozilla/5.0 NodeDcLocalizer",
    },
  });

  if (!response.ok) {
    failures.push(`${response.status} ${item.url.href}`);
    return;
  }

  const buffer = Buffer.from(await response.arrayBuffer());
  await writeFile(outPath, buffer);
}

async function main() {
  await mkdir(targetDir, { recursive: true });
  await mkdir(path.join(targetDir, "originals"), { recursive: true });

  const pageSources = new Map();
  for (const page of pages) {
    const sourcePath = path.join(sourceDir, page);
    const source = await readFile(sourcePath, "utf8");
    pageSources.set(page, source);
    await copyFile(sourcePath, path.join(targetDir, "originals", page));

    const origPath = path.join(sourceDir, page.replace(/\.html$/, ".orig"));
    if (existsSync(origPath)) {
      await copyFile(origPath, path.join(targetDir, "originals", path.basename(origPath)));
    }

    for (const rawUrl of extractUrls(source)) addAsset(rawUrl);
  }

  for (let index = 0; index < queue.length; index += 1) {
    const item = queue[index];
    await downloadAsset(item);

    const ext = extensionFor(item.url);
    if (ext === ".css" || ext === ".js") {
      const localPath = path.join(targetDir, item.localPath);
      if (!existsSync(localPath)) continue;

      const source = await readFile(localPath, "utf8");
      for (const rawUrl of extractUrls(source)) addAsset(rawUrl, item.url.href);
    }
  }

  for (const [remote, local] of assetMap.entries()) {
    const url = new URL(remote);
    const ext = extensionFor(url);
    if (ext !== ".css" && ext !== ".js") continue;

    const localPath = path.join(targetDir, local);
    if (!existsSync(localPath)) continue;

    const source = await readFile(localPath, "utf8");
    const rewritten = rewriteUrls(source, local, remote);
    await writeFile(localPath, rewritten);
  }

  for (const [page, source] of pageSources.entries()) {
    const rewritten = rewriteUrls(source, page);
    await writeFile(path.join(targetDir, page), rewritten);
  }

  const manifest = {
    generatedAt: new Date().toISOString(),
    sourceDir,
    targetDir,
    pages,
    assets: [...assetMap.entries()].map(([url, localPath]) => ({ url, localPath })),
    failures,
  };

  await writeFile(path.join(targetDir, "asset-manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`);

  await writeFile(
    path.join(targetDir, "README.md"),
    `# NODE.DC Local Working Copy

This repository contains the current NODE.DC base site: a structured local copy of the recovered Webflow page, localized to Russian and prepared for the block/CMS refactor.

The rebuild source is \`${sourceDir}\`.

## Run

\`\`\`bash
cd ${targetDir}
python3 -m http.server 8081 --bind 127.0.0.1
\`\`\`

Open http://127.0.0.1:8081/

## Structure

- \`index.html\`, \`beta-apply.html\`, \`founders.html\`, \`enterprise.html\` - editable local pages.
- \`assets/webflow\` - Webflow CSS, JS, and image assets.
- \`assets/custom\` - custom NODE.DC CSS/JS published on Vercel.
- \`assets/media\` - downloaded videos from ssscript.dev.
- \`assets/vendor\` - third-party browser scripts.
- \`docs/\` - refactor notes, block model draft, and admin plan.
- \`originals\` - untouched files copied from the original wget download.
- \`asset-manifest.json\` - source URL to local file mapping.
- \`tools/build-local-copy.mjs\` - rebuild script. It uses \`NODEDC_SOURCE_DIR\` when provided, otherwise the original local rescue folder.
- \`tools/localize-ru.mjs\` - Russian localization and NODE.DC brand patch script.

External product/API/documentation links are intentionally left as external URLs.
`
  );

  console.log(`Pages: ${pages.length}`);
  console.log(`Assets mapped: ${assetMap.size}`);
  console.log(`Failures: ${failures.length}`);
  for (const failure of failures) console.log(`FAILED ${failure}`);
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});
