import { basename, dirname, relative } from "node:path";
import { readdir, readFile, stat } from "node:fs/promises";

export type LoadedPluginCommand = {
  name: string;
  path: string;
  content: string;
  frontmatter: Record<string, unknown>;
  isSkill: boolean;
};

export async function loadPluginCommands(options: {
  pluginName: string;
  baseDir: string;
}): Promise<LoadedPluginCommand[]> {
  const files = await collectMarkdownFiles(options.baseDir);
  return Promise.all(
    files.map(async (filePath) => {
      const raw = await readFile(filePath, "utf8");
      const parsed = parseMarkdownFrontmatter(raw);
      return {
        name: getPluginCommandName(options.pluginName, filePath, options.baseDir),
        path: filePath,
        content: parsed.content,
        frontmatter: parsed.frontmatter,
        isSkill: isSkillFile(filePath),
      };
    }),
  );
}

export function getPluginCommandName(pluginName: string, filePath: string, baseDir: string): string {
  const baseName = isSkillFile(filePath) ? basename(dirname(filePath)) : basename(filePath).replace(/\.md$/iu, "");
  const namespaceRoot = isSkillFile(filePath) ? dirname(dirname(filePath)) : dirname(filePath);
  const namespace = relative(baseDir, namespaceRoot)
    .split(/[\\/]/u)
    .filter(Boolean)
    .join(":");

  return namespace ? `${pluginName}:${namespace}:${baseName}` : `${pluginName}:${baseName}`;
}

function isSkillFile(filePath: string): boolean {
  return /^skill\.md$/iu.test(basename(filePath));
}

async function collectMarkdownFiles(directory: string): Promise<string[]> {
  const output: string[] = [];
  let entries: string[];
  try {
    entries = await readdir(directory);
  } catch {
    return output;
  }

  for (const entry of entries) {
    const fullPath = `${directory}/${entry}`;
    let entryStat;
    try {
      entryStat = await stat(fullPath);
    } catch {
      continue;
    }
    if (entryStat.isDirectory()) {
      output.push(...await collectMarkdownFiles(fullPath));
    } else if (/\.md$/iu.test(entry)) {
      output.push(fullPath);
    }
  }
  return output;
}

function parseMarkdownFrontmatter(raw: string): { frontmatter: Record<string, unknown>; content: string } {
  if (!raw.startsWith("---\n")) {
    return { frontmatter: {}, content: raw };
  }
  const end = raw.indexOf("\n---\n", 4);
  if (end === -1) {
    return { frontmatter: {}, content: raw };
  }
  const frontmatter: Record<string, unknown> = {};
  for (const line of raw.slice(4, end).split("\n")) {
    const separator = line.indexOf(":");
    if (separator === -1) {
      continue;
    }
    const key = line.slice(0, separator).trim();
    const value = line.slice(separator + 1).trim();
    if (key) {
      frontmatter[key] = parseScalar(value);
    }
  }
  return { frontmatter, content: raw.slice(end + 5) };
}

function parseScalar(value: string): unknown {
  if (value === "true") return true;
  if (value === "false") return false;
  const numberValue = Number(value);
  if (value !== "" && Number.isFinite(numberValue)) return numberValue;
  return value.replace(/^["']|["']$/gu, "");
}