/**
 * Remark plugin that converts Obsidian-style [[wiki-links]] to standard
 * Markdown links by resolving filenames against the content directory.
 *
 * Supported syntax:
 *   [[filename]]              → [filename](/url/to/filename/)
 *   [[filename|display text]] → [display text](/url/to/filename/)
 *   [[filename#heading]]      → heading fragment is preserved in the URL
 *   ![[image.jpg]]            → ![image.jpg](./relative/path/image.jpg)
 *   ![[image.jpg|alt text]]   → ![alt text](./relative/path/image.jpg)
 *
 * Content sources and their URL prefixes:
 *   src/content/blog/posts        → /blog/{slug}/
 *   src/content/notes             → /notes/{slug}/
 *   src/content/projects/project  → /projects/{slug}/
 */

import { readdirSync } from "node:fs";
import { dirname, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { SKIP, visit } from "unist-util-visit";

const __dirname = dirname(fileURLToPath(import.meta.url));

const SOURCES = [
  {
    base: resolve(__dirname, "../content/blog/posts"),
    urlPrefix: "/blog",
  },
  {
    base: resolve(__dirname, "../content/notes"),
    urlPrefix: "/notes",
  },
  {
    base: resolve(__dirname, "../content/projects/project"),
    urlPrefix: "/projects",
  },
];

const IMAGE_EXT_RE = /\.(jpe?g|png|gif|webp|svg|avif)$/i;

function buildFileIndex() {
  const map = new Map();
  for (const { base, urlPrefix } of SOURCES) {
    let files;
    try {
      files = readdirSync(base, { recursive: true });
    } catch {
      continue; // directory may not exist yet
    }
    for (const file of files) {
      if (!/\.(md|mdx)$/.test(file)) continue;
      const slug = file.replace(/\.(md|mdx)$/, "").replace(/\\/g, "/");
      const filename = slug.split("/").pop();
      // First match wins — filename must be unique across collections
      if (!map.has(filename)) {
        map.set(filename, `${urlPrefix}/${slug}/`);
      }
    }
  }
  return map;
}

function buildImageIndex() {
  const map = new Map();
  for (const { base } of SOURCES) {
    let files;
    try {
      files = readdirSync(base, { recursive: true });
    } catch {
      continue;
    }
    for (const file of files) {
      if (!IMAGE_EXT_RE.test(file)) continue;
      const filename = file.replace(/\\/g, "/").split("/").pop();
      if (!map.has(filename)) {
        map.set(filename, resolve(base, file));
      }
    }
  }
  return map;
}

// Build once per process, not per file
const fileIndex = buildFileIndex();
const imageIndex = buildImageIndex();

// Matches optional leading ! and [[...]] content
const WIKI_LINK_RE = /(!?)\[\[([^\]]+)\]\]/g;

export function remarkObsidianLinks() {
  return (tree, file) => {
    visit(tree, "text", (node, index, parent) => {
      if (!WIKI_LINK_RE.test(node.value)) return;
      WIKI_LINK_RE.lastIndex = 0;

      const nodes = [];
      let last = 0;
      let match = WIKI_LINK_RE.exec(node.value);

      while (match !== null) {
        if (match.index > last) {
          nodes.push({
            type: "text",
            value: node.value.slice(last, match.index),
          });
        }

        const embed = match[1] === "!";
        const inner = match[2];
        const pipeIdx = inner.indexOf("|");
        const ref = pipeIdx === -1 ? inner : inner.slice(0, pipeIdx);
        const label =
          pipeIdx === -1
            ? ref.split("#")[0].trim()
            : inner.slice(pipeIdx + 1).trim();
        const [filename, heading] = ref.trim().split("#");
        const trimmedFilename = filename.trim();

        // Image embed: ![[image.ext]]
        if (embed && IMAGE_EXT_RE.test(trimmedFilename)) {
          const absImagePath = imageIndex.get(trimmedFilename);
          if (absImagePath && file?.path) {
            const relPath =
              "./" +
              relative(dirname(file.path), absImagePath).replace(/\\/g, "/");
            nodes.push({
              type: "image",
              url: relPath,
              alt: label || trimmedFilename,
              title: null,
            });
          } else {
            // Fallback: emit as link
            nodes.push({
              type: "link",
              url: `#${trimmedFilename}`,
              title: null,
              children: [{ type: "text", value: label || trimmedFilename }],
            });
          }
        } else {
          // Regular wiki-link
          const base = fileIndex.get(trimmedFilename);
          const url = base
            ? heading
              ? `${base}#${heading.trim()}`
              : base
            : `#${trimmedFilename}`;

          nodes.push({
            type: "link",
            url,
            title: null,
            data: {
              hName: "a",
              hProperties: {
                href: url,
                target: "_blank",
                rel: "noopener noreferrer",
              },
              hChildren: [{ type: "text", value: label }],
            },
            children: [{ type: "text", value: label }],
          });
        }

        last = match.index + match[0].length;
        match = WIKI_LINK_RE.exec(node.value);
      }

      if (last < node.value.length) {
        nodes.push({ type: "text", value: node.value.slice(last) });
      }

      parent.children.splice(index, 1, ...nodes);
      return [SKIP, index + nodes.length];
    });
  };
}
