import { fromMarkdown } from "mdast-util-from-markdown";
import { toString } from "mdast-util-to-string";
import type { Heading, List, Root, RootContent } from "mdast";

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

export interface ParsedDependency {
  module: string;
  entity: string;
}

export interface ParsedCommandDoc {
  commandName: string;
  errors: ParsedError[];
  externalDependencies: ParsedDependency[];
}

export function parseCommandDoc(fileName: string, markdown: string): ParsedCommandDoc {
  const commandName = fileName.charAt(0).toLowerCase() + fileName.slice(1);
  const tree = fromMarkdown(markdown);

  return {
    commandName,
    errors: parseErrorScenarios(tree),
    externalDependencies: parseExternalDependencies(tree),
  };
}

export function errorCodeToClassName(code: string): string {
  const pascal = code
    .toLowerCase()
    .split("_")
    .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
    .join("");
  return pascal + "Error";
}

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

function isList(node: RootContent): node is List {
  return node.type === "list";
}

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;
}

const ERROR_PATTERN = /^([A-Z_]+):\s*(.+)$/;

function parseErrorScenarios(tree: Root): ParsedError[] {
  const nodes = getNodesUnderHeading(tree, "Error Scenarios");
  const errors: ParsedError[] = [];

  for (const node of nodes) {
    if (!isList(node)) continue;

    for (const item of node.children) {
      const text = toString(item);
      const match = ERROR_PATTERN.exec(text);
      if (match) {
        errors.push({ code: match[1], description: match[2].trim() });
      }
    }
  }

  return errors;
}

const DEPENDENCY_PATTERN = /^([^:]+)::(.+)$/;

function parseExternalDependencies(tree: Root): ParsedDependency[] {
  const nodes = getNodesUnderHeading(tree, "External Dependencies");
  const deps: ParsedDependency[] = [];

  for (const node of nodes) {
    if (!isList(node)) continue;

    for (const item of node.children) {
      const firstChild = item.children[0];
      if (firstChild?.type !== "paragraph") continue;

      for (const inline of firstChild.children) {
        if (inline.type === "link" || inline.type === "linkReference") {
          const linkText = toString(inline);
          const match = DEPENDENCY_PATTERN.exec(linkText);
          if (match) {
            deps.push({ module: match[1], entity: match[2] });
          }
        }
      }
    }
  }

  return deps;
}
