/**
 * Represents a parsed wiki-style link from markdown content
 */
export interface WikiLink {
  /** The raw matched text including brackets (e.g., "[[Page Name]]") */
  raw: string;
  /** The target page name or slug */
  target: string;
  /** The display text (may differ from target if using | syntax) */
  displayText: string;
  /** Start position of the link in the original content */
  start: number;
  /** End position of the link in the original content */
  end: number;
}

/**
 * Represents a region in the content that should be excluded from parsing
 */
interface ExcludedRegion {
  start: number;
  end: number;
}

/**
 * Parse wiki-style [[links]] from markdown content.
 *
 * Supports:
 * - [[Page Name]] - link by title
 * - [[Page Name|display text]] - link with custom display
 * - [[page-slug]] - link by slug
 *
 * Handles edge cases:
 * - Nested brackets within link text
 * - Links in code blocks (excluded from parsing)
 * - Escaped brackets (excluded from parsing)
 *
 * @param content - The markdown content to parse
 * @returns Array of parsed wiki links with position information
 */
export function parseWikiLinks(content: string): WikiLink[] {
  if (!content || content.length === 0) {
    return [];
  }

  const excludedRegions = findExcludedRegions(content);
  const links: WikiLink[] = [];

  // Manual parsing to handle complex bracket scenarios
  let i = 0;
  while (i < content.length) {
    // Look for [[
    if (i < content.length - 1 && content[i] === "[" && content[i + 1] === "[") {
      // Check if preceded by escape character
      if (i > 0 && content[i - 1] === "\\") {
        i++;
        continue;
      }

      // Check if preceded by another [ (would make [[[)
      if (i > 0 && content[i - 1] === "[") {
        i++;
        continue;
      }

      // Check if followed by another [ (would make [[[)
      if (i + 2 < content.length && content[i + 2] === "[") {
        i++;
        continue;
      }

      const start = i;
      i += 2; // Skip past [[

      // Find the closing ]]
      let innerContent = "";
      let foundClosing = false;

      while (i < content.length - 1) {
        // Check for ]]
        if (content[i] === "]" && content[i + 1] === "]") {
          foundClosing = true;
          break;
        }
        const char = content[i];
        if (char !== undefined) {
          innerContent += char;
        }
        i++;
      }

      if (!foundClosing) {
        // No closing brackets found, continue searching
        continue;
      }

      const end = i + 2; // Include the ]]
      const raw = content.substring(start, end);

      // Skip if this link is in an excluded region
      if (isInExcludedRegion(start, end, excludedRegions)) {
        i += 2; // Move past the ]]
        continue;
      }

      // Parse the inner content to extract target and display text
      const parsed = parseInnerContent(innerContent);
      if (!parsed) {
        i += 2; // Move past the ]]
        continue;
      }

      links.push({
        raw,
        target: parsed.target,
        displayText: parsed.displayText,
        start,
        end,
      });

      i += 2; // Move past the ]]
    } else {
      i++;
    }
  }

  return links;
}

/**
 * Parse the inner content of a wiki link to extract target and display text
 */
function parseInnerContent(content: string): { target: string; displayText: string } | null {
  // Check for pipe separator
  const pipeIndex = content.indexOf("|");

  let target: string;
  let displayText: string;

  if (pipeIndex !== -1) {
    // Has display text
    target = content.substring(0, pipeIndex).trim();
    displayText = content.substring(pipeIndex + 1).trim();

    // If display text is empty after trim, use target
    if (displayText === "") {
      displayText = target;
    }
  } else {
    // No display text, target and display are the same
    target = content.trim();
    displayText = target;
  }

  // Reject if target is empty or whitespace-only
  if (target === "") {
    return null;
  }

  return { target, displayText };
}

/**
 * Find all regions that should be excluded from wiki link parsing
 * (code blocks, inline code, etc.)
 */
function findExcludedRegions(content: string): ExcludedRegion[] {
  const regions: ExcludedRegion[] = [];

  // Find fenced code blocks (``` ... ```)
  const fencedCodePattern = /```[\s\S]*?```/g;
  let match: RegExpExecArray | null;

  while ((match = fencedCodePattern.exec(content)) !== null) {
    regions.push({
      start: match.index,
      end: match.index + match[0].length,
    });
  }

  // Find indented code blocks (4 spaces or 1 tab at line start)
  const lines = content.split("\n");
  let currentIndex = 0;
  let inIndentedBlock = false;
  let blockStart = 0;

  for (const line of lines) {
    const lineStart = currentIndex;
    const lineEnd = currentIndex + line.length;

    // Check if line is indented (4 spaces or tab)
    const isIndented = line.startsWith("    ") || line.startsWith("\t");
    const isEmpty = line.trim() === "";

    if (isIndented && !inIndentedBlock) {
      // Start of indented block
      inIndentedBlock = true;
      blockStart = lineStart;
    } else if (!isIndented && !isEmpty && inIndentedBlock) {
      // End of indented block (non-empty, non-indented line)
      regions.push({
        start: blockStart,
        end: lineStart,
      });
      inIndentedBlock = false;
    }

    currentIndex = lineEnd + 1; // +1 for newline character
  }

  // Handle case where indented block extends to end of content
  if (inIndentedBlock) {
    regions.push({
      start: blockStart,
      end: content.length,
    });
  }

  // Find inline code (` ... `)
  // This is tricky because we need to track state
  let inInlineCode = false;
  let inlineStart = 0;

  for (let i = 0; i < content.length; i++) {
    if (content[i] === "`") {
      // Check if it's escaped
      if (i > 0 && content[i - 1] === "\\") {
        continue;
      }

      // Check if we're already in a fenced code block or indented block
      if (isInExcludedRegion(i, i + 1, regions)) {
        continue;
      }

      if (!inInlineCode) {
        inInlineCode = true;
        inlineStart = i;
      } else {
        // End of inline code
        regions.push({
          start: inlineStart,
          end: i + 1,
        });
        inInlineCode = false;
      }
    }
  }

  // Handle unclosed inline code (extends to end of content)
  if (inInlineCode) {
    regions.push({
      start: inlineStart,
      end: content.length,
    });
  }

  // Sort regions by start position for efficient checking
  regions.sort((a, b) => a.start - b.start);

  return regions;
}

/**
 * Check if a position range is within any excluded region
 */
function isInExcludedRegion(start: number, end: number, regions: ExcludedRegion[]): boolean {
  for (const region of regions) {
    // Check if the range overlaps with this excluded region
    if (start < region.end && end > region.start) {
      return true;
    }
  }
  return false;
}
