package main

import (
	"bytes"
	"regexp"

	"github.com/yuin/goldmark/ast"
	"github.com/yuin/goldmark/text"
)

var sectionHeadingRe = regexp.MustCompile(`(?m)^#{1,6} `)

// splitSections splits raw markdown into sections.
// Section 0 is any content before the first heading.
// Each subsequent section begins at a heading line and runs to the next.
func splitSections(raw []byte) [][]byte {
	locs := sectionHeadingRe.FindAllIndex(raw, -1)
	if len(locs) == 0 {
		return [][]byte{raw}
	}
	sections := make([][]byte, 0, len(locs)+1)
	prev := 0
	for _, loc := range locs {
		sections = append(sections, raw[prev:loc[0]])
		prev = loc[0]
	}
	sections = append(sections, raw[prev:])
	return sections
}

// headingIDs returns the auto-generated id of every heading in raw markdown,
// in document order. The kth heading (1-indexed) corresponds to section k from
// splitSections. Uses the package-level goldmark parser so duplicate-id
// numbering matches what the renderer emits.
func headingIDs(raw []byte) []string {
	doc := md.Parser().Parse(text.NewReader(raw))
	var ids []string
	ast.Walk(doc, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
		if !entering {
			return ast.WalkContinue, nil
		}
		if _, ok := n.(*ast.Heading); ok {
			if v, ok := n.AttributeString("id"); ok {
				if b, ok := v.([]byte); ok {
					ids = append(ids, string(b))
				}
			}
		}
		return ast.WalkContinue, nil
	})
	return ids
}

// joinSections reassembles sections produced by splitSections.
// Inserts a newline between sections when a non-empty section lacks a
// trailing newline, so an edited section cannot inline the next heading.
func joinSections(sections [][]byte) []byte {
	var buf bytes.Buffer
	for i, s := range sections {
		buf.Write(s)
		if i < len(sections)-1 && len(s) > 0 && s[len(s)-1] != '\n' {
			buf.WriteByte('\n')
		}
	}
	return buf.Bytes()
}
