import fs from "node:fs";
import path from "node:path";

const root = path.resolve("target/doc-md/hadron_sdk");

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function walk(dir) {
  const out = [];
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
    const full = path.join(dir, entry.name);
    if (entry.isDirectory()) out.push(...walk(full));
    else if (entry.isFile() && full.endsWith(".md")) out.push(full);
  }
  return out;
}

function firstHeading(content) {
  for (const line of content.split("\n")) {
    if (line.startsWith("# ")) return line.slice(2).trim();
  }
  return null;
}

function slugFromPath(file) {
  const rel = path.relative(root, file).replaceAll(path.sep, "/");
  return rel.replace(/\.md$/i, "");
}

/** Derive a human-readable title from the first heading or filename. */
function normalizeTitle(raw, fallbackName) {
  const cleaned = (raw || fallbackName)
    .replace(/^Module:\s*/i, "")
    .replace(/^hadron_sdk$/i, "Hadron SDK")
    .trim();
  return cleaned || fallbackName;
}

/** Pick a description based on the doc's path within the crate. */
function descriptionForSlug(slug, title) {
  if (slug === "index") return "API reference overview for the Hadron Rust SDK.";
  if (slug === "hadron_sdk") return "Top-level module re-exports for hadron-sdk.";
  if (slug === "hadron") return "Main Hadron struct and pool interaction methods.";
  if (slug === "accounts") return "Account data decoding functions.";
  if (slug === "types") return "Type definitions, parameter structs, and enums.";
  if (slug === "constants") return "Program constants and configuration defaults.";

  if (slug.startsWith("helpers/"))
    return `Helper utilities — ${title.replace(/^helpers::/, "")}.`;
  if (slug === "helpers") return "Helper utility modules (derive, math).";

  if (slug.startsWith("instructions/"))
    return `Instruction builders for ${title.replace(/^instructions::/, "")}.`;

  return "Autogenerated Rust SDK reference documentation.";
}

// ---------------------------------------------------------------------------
// Transforms
// ---------------------------------------------------------------------------

/** Convert relative .md links to absolute Mintlify routes. */
function patchLinks(content, file) {
  return content.replace(/\]\(([^)]+)\)/g, (full, href) => {
    if (!href || href.startsWith("http") || href.startsWith("#")) return full;

    const [rawPath, hash = ""] = href.split("#");
    if (!rawPath || !rawPath.endsWith(".md")) return full;

    const fromDir = path.posix.dirname(
      path.relative(root, file).replaceAll(path.sep, "/"),
    );
    const resolved = path.posix
      .normalize(path.posix.join(fromDir, rawPath))
      .replace(/^\.\//, "")
      .replace(/\.md$/i, "");

    const target = `/sdk/rust/reference/api/${resolved}${hash ? `#${hash}` : ""}`;
    return `](${target})`;
  });
}

/** Remove the bold breadcrumb line at the top (e.g. **hadron_sdk > module**). */
function stripBreadcrumbs(content) {
  return content.replace(/^\*\*hadron_sdk(?:\s*>\s*[^*]+)*\*\*\s*\n+/gm, "");
}

/**
 * Remove verbose trait implementations for derive macros that add noise.
 * Matches the "**Trait Implementations:**" block and strips Clone, Debug,
 * Copy, Default, PartialEq entries (keeps Display, Error, TryFrom, etc.).
 */
function cleanTraitImpls(content) {
  const lines = content.split("\n");
  const out = [];
  let i = 0;

  while (i < lines.length) {
    // Detect "**Trait Implementations:**" section
    if (lines[i].trim() === "**Trait Implementations:**") {
      const implLines = [lines[i]];
      i++;

      // Skip blank line after header
      while (i < lines.length && lines[i].trim() === "") {
        implLines.push(lines[i]);
        i++;
      }

      // Collect impl entries: groups starting with "- **TraitName**"
      const kept = [];
      while (i < lines.length && lines[i].startsWith("- **")) {
        const traitMatch = lines[i].match(/^- \*\*(\w+)\*\*/);
        const traitName = traitMatch ? traitMatch[1] : "";
        const noisy = ["Clone", "Debug", "Copy", "Default", "PartialEq"].includes(
          traitName,
        );

        const group = [lines[i]];
        i++;
        // Collect indented continuation lines (sub-items of this trait)
        while (i < lines.length && lines[i].startsWith("  ")) {
          group.push(lines[i]);
          i++;
        }
        // Skip blank lines between entries
        while (i < lines.length && lines[i].trim() === "") {
          i++;
        }

        if (!noisy) kept.push(...group, "");
      }

      if (kept.length > 0) {
        out.push(...implLines);
        out.push("");
        out.push(...kept);
      }
      // If all impls were noisy, drop the entire section
      continue;
    }

    // Also strip standalone "**Traits:** Copy" / "**Traits:** Eq, Copy" lines
    if (/^\*\*Traits:\*\*\s/.test(lines[i])) {
      // Keep the line only if it mentions traits beyond the noisy set
      const traitList = lines[i]
        .replace(/^\*\*Traits:\*\*\s*/, "")
        .split(",")
        .map((t) => t.trim());
      const meaningful = traitList.filter(
        (t) => !["Clone", "Debug", "Copy", "Default", "PartialEq", "Eq"].includes(t),
      );
      if (meaningful.length > 0) {
        out.push(`**Traits:** ${meaningful.join(", ")}`);
      }
      i++;
      continue;
    }

    out.push(lines[i]);
    i++;
  }

  return out.join("\n");
}

/** Shorten fully-qualified paths in code blocks and signatures. */
function shortenPaths(content) {
  const replacements = [
    [/solana_sdk::pubkey::Pubkey/g, "Pubkey"],
    [/solana_sdk::instruction::Instruction/g, "Instruction"],
    [/crate::types::/g, ""],
    [/crate::constants::/g, ""],
    [/crate::accounts::/g, ""],
    [/crate::helpers::derive::/g, ""],
    [/crate::helpers::math::/g, ""],
    [/hadron_sdk::types::/g, ""],
    [/hadron_sdk::constants::/g, ""],
    [/hadron_sdk::accounts::/g, ""],
    [/hadron_sdk::hadron::/g, ""],
    [/hadron_sdk::helpers::derive::/g, ""],
    [/hadron_sdk::helpers::math::/g, ""],
    [/hadron_sdk::instructions::\w+::/g, ""],
    // Clean up self references in trait impls
    [/self: &Self/g, "&self"],
    [/& mut \$crate::fmt::Formatter/g, "&mut fmt::Formatter"],
    [/\$crate::fmt::Result/g, "fmt::Result"],
    [/::core::fmt::Formatter/g, "fmt::Formatter"],
    [/::core::fmt::Result/g, "fmt::Result"],
  ];

  let result = content;
  for (const [pattern, replacement] of replacements) {
    result = result.replace(pattern, replacement);
  }
  return result;
}

/** Remove duplicate blank lines. */
function dedupeBlankLines(content) {
  return content.replace(/\n{3,}/g, "\n\n");
}

/** Add YAML frontmatter. */
function withFrontmatter(content, title, description) {
  // Strip existing frontmatter if present
  if (content.startsWith("---\n")) {
    const end = content.indexOf("\n---\n", 4);
    if (end !== -1) content = content.slice(end + 5);
  }

  const escaped = (s) => s.replaceAll('"', '\\"');
  return `---\ntitle: "${escaped(title)}"\ndescription: "${escaped(description)}"\n---\n\n${content.trimStart()}`;
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------

if (!fs.existsSync(root)) {
  console.error(`Missing generated docs directory: ${root}`);
  console.error("Run `cargo +nightly doc-md` first.");
  process.exit(1);
}

let count = 0;
for (const file of walk(root)) {
  const current = fs.readFileSync(file, "utf8");
  const heading = firstHeading(current);
  const fallback = path.basename(file, ".md");
  const title = normalizeTitle(heading, fallback);
  const slug = slugFromPath(file);
  const description = descriptionForSlug(slug, title);

  let processed = stripBreadcrumbs(current);
  processed = patchLinks(processed, file);
  processed = cleanTraitImpls(processed);
  processed = shortenPaths(processed);
  processed = dedupeBlankLines(processed);
  processed = withFrontmatter(processed, title, description);

  fs.writeFileSync(file, processed);
  count++;
}

console.log(`Prepared ${count} Rust SDK docs for Mintlify.`);
