import type { Section } from "./models"
import { normalizeSectionTitle } from "./section-normalizer"

export class SddMarkdownParser {
  private lines: string[]
  private codeFenceMask: boolean[]

  constructor(content: string) {
    const normalized = content.replace(/\r\n?/g, "\n")
    this.lines = normalized.split("\n")
    this.codeFenceMask = this.buildCodeFenceMask(this.lines)
  }

  private parseFenceOpener(line: string): { marker: string; length: number } | undefined {
    const match = line.match(/^\s*(```+|~~~+)/)
    return match ? { marker: match[1][0], length: match[1].length } : undefined
  }

  private isFenceCloser(line: string, fence: { marker: string; length: number }): boolean {
    return new RegExp(`^\\s*${fence.marker}{${fence.length},}\\s*$`).test(line)
  }

  private buildCodeFenceMask(lines: string[]): boolean[] {
    const mask = new Array(lines.length).fill(false)
    let activeFence: { marker: string; length: number } | undefined = undefined
    for (let i = 0; i < lines.length; i++) {
      if (!activeFence) {
        activeFence = this.parseFenceOpener(lines[i])
        if (activeFence) {
          mask[i] = true
        }
        continue
      }
      mask[i] = true
      if (this.isFenceCloser(lines[i], activeFence)) {
        activeFence = undefined
      }
    }
    return mask
  }

  parseSections(): Section[] {
    const sections: Section[] = []
    const stack: Section[] = []
    for (let i = 0; i < this.lines.length; i++) {
      if (this.codeFenceMask[i]) {
        continue
      }
      const match = this.lines[i].match(/^(#{1,6})\s+(.+)$/)
      if (match) {
        const level = match[1].length
        const title = match[2].trim()
        const content = this.getContentUntilNextHeader(i + 1, level)
        const section: Section = {
          level,
          title,
          normalizedTitle: normalizeSectionTitle(title),
          content,
          children: [],
          lineNumber: i + 1,
        }
        while (stack.length > 0 && stack[stack.length - 1].level >= level) {
          stack.pop()
        }
        if (stack.length === 0) {
          sections.push(section)
        } else {
          stack[stack.length - 1].children.push(section)
        }
        stack.push(section)
      }
    }
    return sections
  }

  private getContentUntilNextHeader(startLine: number, currentLevel: number): string {
    const contentLines: string[] = []
    for (let i = startLine; i < this.lines.length; i++) {
      if (this.codeFenceMask[i]) {
        contentLines.push(this.lines[i])
        continue
      }
      const headerMatch = this.lines[i].match(/^(#{1,6})\s+/)
      if (headerMatch && headerMatch[1].length <= currentLevel) {
        break
      }
      contentLines.push(this.lines[i])
    }
    return contentLines.join("\n").trim()
  }

  findSection(sections: Section[], predicate: (s: Section) => boolean): Section | undefined {
    for (const section of sections) {
      if (predicate(section)) {
        return section
      }
      const found = this.findSection(section.children, predicate)
      if (found) {
        return found
      }
    }
    return undefined
  }

  findSections(sections: Section[], predicate: (s: Section) => boolean): Section[] {
    const result: Section[] = []
    for (const section of sections) {
      if (predicate(section)) {
        result.push(section)
      }
      result.push(...this.findSections(section.children, predicate))
    }
    return result
  }
}