import type { Heading, RootContent, Root } from "mdast";
import { fromMarkdown } from "mdast-util-from-markdown";
import { toString } from "mdast-util-to-string";
import { parseMarkdownTable } from "../../../lib/parse-markdown-table";

export interface ParsedModuleCommand {
  module: string;
  command: string;
}

export interface ParsedResolverError {
  code: string;
  description: string;
}

export interface ParsedResolverDoc {
  resolverName: string;
  moduleCommands: ParsedModuleCommand[];
  errors: ParsedResolverError[];
}

const MODULE_COMMAND_PATTERN = /([a-z][\w-]*)\.(\w+)/;

export function parseResolverDoc(fileName: string, markdown: string): ParsedResolverDoc {
  const resolverName = fileName.charAt(0).toLowerCase() + fileName.slice(1);
  const tree = fromMarkdown(markdown);

  return {
    resolverName,
    moduleCommands: parseModuleCommands(tree),
    errors: parseErrors(markdown),
  };
}

function isHeading(node: RootContent): node is Heading {
  return node.type === "heading";
}

function getNodesUnderHeading(tree: Root, headingText: string): RootContent[] {
  const nodes: RootContent[] = [];
  let collecting = false;

  for (const node of tree.children) {
    if (isHeading(node)) {
      if (collecting) break;
      if (node.depth === 2 && toString(node) === headingText) {
        collecting = true;
        continue;
      }
    }
    if (collecting) {
      nodes.push(node);
    }
  }

  return nodes;
}

function parseModuleCommands(tree: Root): ParsedModuleCommand[] {
  const nodes = getNodesUnderHeading(tree, "Modules Commands Used");
  const commands: ParsedModuleCommand[] = [];

  for (const node of nodes) {
    if (node.type !== "list") continue;

    for (const item of node.children) {
      const text = toString(item);
      const match = MODULE_COMMAND_PATTERN.exec(text);
      if (match) {
        commands.push({ module: match[1], command: match[2] });
      }
    }
  }

  return commands;
}

function parseErrors(markdown: string): ParsedResolverError[] {
  return parseMarkdownTable(markdown, "## Exception Handling")
    .filter((cells) => cells.length >= 2 && cells[0])
    .map(([code, description]) => ({ code, description }));
}
