package lexgen

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"sort"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/johnkerl/pgpg/go/lib/pkg/asts"
	"github.com/johnkerl/pgpg/go/lib/pkg/parsers"
)

// LexTableOptions configures lexer table generation from a grammar.
type LexTableOptions struct {
	// SourceName is used in error messages (e.g. file path). Empty means "".
	SourceName string
}

// EncodeOptions configures JSON encoding of tables.
type EncodeOptions struct {
	// Sort enables deterministic map key order for stable, diff-friendly output.
	// When false, encoding is faster but key order is nondeterministic.
	Sort bool
}

// Tables captures DFA transitions and accepting actions for a lexer.
type Tables struct {
	StartState  int                       `json:"start_state"`
	Transitions map[int][]RangeTransition `json:"transitions"`
	Actions     map[int]string            `json:"actions"`
	Rules       map[string]string         `json:"rules,omitempty"`
	Metadata    map[string]string         `json:"metadata,omitempty"`
}

// RangeTransition is a DFA transition on an inclusive rune range.
type RangeTransition struct {
	From rune `json:"from"`
	To   rune `json:"to"`
	Next int  `json:"next"`
}

// MarshalJSON ensures deterministic map ordering for stable output.
func (tables *Tables) MarshalJSON() ([]byte, error) {
	return marshalTablesDeterministic(tables)
}

func marshalTablesDeterministic(tables *Tables) ([]byte, error) {
	if tables == nil {
		return []byte("null"), nil
	}
	var fields []jsonField

	startStateBytes, err := json.Marshal(tables.StartState)
	if err != nil {
		return nil, err
	}
	fields = append(fields, jsonField{name: "start_state", value: startStateBytes})

	transitionsBytes, err := marshalMapIntRangeTransitions(tables.Transitions)
	if err != nil {
		return nil, err
	}
	fields = append(fields, jsonField{name: "transitions", value: transitionsBytes})

	actionsBytes, err := marshalMapIntString(tables.Actions)
	if err != nil {
		return nil, err
	}
	fields = append(fields, jsonField{name: "actions", value: actionsBytes})

	if len(tables.Rules) > 0 {
		rulesBytes, err := marshalMapStringString(tables.Rules)
		if err != nil {
			return nil, err
		}
		fields = append(fields, jsonField{name: "rules", value: rulesBytes})
	}

	if len(tables.Metadata) > 0 {
		metadataBytes, err := marshalMapStringString(tables.Metadata)
		if err != nil {
			return nil, err
		}
		fields = append(fields, jsonField{name: "metadata", value: metadataBytes})
	}

	return marshalOrderedFields(fields), nil
}

type jsonField struct {
	name  string
	value []byte
}

func marshalOrderedFields(fields []jsonField) []byte {
	var buf bytes.Buffer
	buf.WriteByte('{')
	for i, field := range fields {
		if i > 0 {
			buf.WriteByte(',')
		}
		buf.WriteString(strconv.Quote(field.name))
		buf.WriteByte(':')
		buf.Write(field.value)
	}
	buf.WriteByte('}')
	return buf.Bytes()
}

func marshalMapIntRangeTransitions(m map[int][]RangeTransition) ([]byte, error) {
	if m == nil {
		return []byte("null"), nil
	}
	keys := make([]int, 0, len(m))
	for key := range m {
		keys = append(keys, key)
	}
	sort.Ints(keys)

	var buf bytes.Buffer
	buf.WriteByte('{')
	for i, key := range keys {
		if i > 0 {
			buf.WriteByte(',')
		}
		buf.WriteString(strconv.Quote(strconv.Itoa(key)))
		buf.WriteByte(':')
		valueBytes, err := json.Marshal(m[key])
		if err != nil {
			return nil, err
		}
		buf.Write(valueBytes)
	}
	buf.WriteByte('}')
	return buf.Bytes(), nil
}

func marshalMapIntString(m map[int]string) ([]byte, error) {
	if m == nil {
		return []byte("null"), nil
	}
	keys := make([]int, 0, len(m))
	for key := range m {
		keys = append(keys, key)
	}
	sort.Ints(keys)

	var buf bytes.Buffer
	buf.WriteByte('{')
	for i, key := range keys {
		if i > 0 {
			buf.WriteByte(',')
		}
		buf.WriteString(strconv.Quote(strconv.Itoa(key)))
		buf.WriteByte(':')
		valueBytes, err := json.Marshal(m[key])
		if err != nil {
			return nil, err
		}
		buf.Write(valueBytes)
	}
	buf.WriteByte('}')
	return buf.Bytes(), nil
}

func marshalMapStringString(m map[string]string) ([]byte, error) {
	if m == nil {
		return []byte("null"), nil
	}
	keys := make([]string, 0, len(m))
	for key := range m {
		keys = append(keys, key)
	}
	sort.Strings(keys)

	var buf bytes.Buffer
	buf.WriteByte('{')
	for i, key := range keys {
		if i > 0 {
			buf.WriteByte(',')
		}
		buf.WriteString(strconv.Quote(key))
		buf.WriteByte(':')
		valueBytes, err := json.Marshal(m[key])
		if err != nil {
			return nil, err
		}
		buf.Write(valueBytes)
	}
	buf.WriteByte('}')
	return buf.Bytes(), nil
}

// GenerateTablesFromReader reads a BNF grammar from r and produces lexer tables.
// It is a convenience for callers that have an io.Reader (e.g. HTTP body, open file, bytes.Buffer).
// opts may be nil; SourceName is then "".
func GenerateTablesFromReader(r io.Reader, opts *LexTableOptions) (*Tables, error) {
	b, err := io.ReadAll(r)
	if err != nil {
		return nil, fmt.Errorf("read grammar: %w", err)
	}
	return GenerateTables(string(b), opts)
}

// GenerateTables parses an EBNF grammar and produces lexer tables.
// Lexer rules must expand to regex-compatible forms; repeats and references are supported.
// opts may be nil; SourceName is then "".
func GenerateTables(grammarText string, opts *LexTableOptions) (*Tables, error) {
	sourceName := ""
	if opts != nil {
		sourceName = opts.SourceName
	}
	parser := parsers.NewEBNFParserWithSourceName(sourceName)
	ast, err := parser.Parse(strings.NewReader(grammarText))
	if err != nil {
		return nil, err
	}

	ruleDefs, err := extractRuleDefs(ast)
	if err != nil {
		return nil, err
	}

	lexerRuleNames := selectLexerRuleNames(ruleDefs)
	if len(lexerRuleNames) == 0 {
		return nil, fmt.Errorf("no lexer rules found")
	}
	tokenRuleNames := selectTokenRuleNames(ruleDefs)
	if len(tokenRuleNames) == 0 {
		return nil, fmt.Errorf("no lexer token rules found")
	}
	lexerRuleSet := map[string]bool{}
	for _, name := range lexerRuleNames {
		lexerRuleSet[name] = true
	}

	ruleMap := map[string]*asts.ASTNode{}
	for _, rule := range ruleDefs {
		ruleMap[rule.name] = rule.expr
	}

	regexCache := map[string]*regexNode{}
	regexRules := map[string]*regexNode{}
	for _, ruleName := range lexerRuleNames {
		node, err := buildRegexForRule(ruleName, ruleMap, lexerRuleSet, regexCache, map[string]bool{})
		if err != nil {
			return nil, fmt.Errorf("rule %q: %w", ruleName, err)
		}
		if canBeEmpty(node) {
			return nil, fmt.Errorf("rule %q expands to empty literal", ruleName)
		}
		regexRules[ruleName] = node
	}

	nfaBuilder := &nfaBuilder{}
	globalStart := nfaBuilder.newState()
	for i, ruleName := range tokenRuleNames {
		node := regexRules[ruleName]
		fragment, err := nfaBuilder.build(node)
		if err != nil {
			return nil, fmt.Errorf("rule %q: %w", ruleName, err)
		}
		accept := acceptRule{name: ruleName, priority: i}
		for _, state := range fragment.accepts {
			state.accepts = append(state.accepts, accept)
		}
		globalStart.epsilon = append(globalStart.epsilon, fragment.start)
	}

	dfa := buildDFA(globalStart)

	transitions := map[int][]RangeTransition{}
	actions := map[int]string{}
	for _, state := range dfa.states {
		if len(state.transitions) > 0 {
			transitions[state.id] = mergeRangeTransitions(state.transitions)
		}
		if state.accept != nil {
			actions[state.id] = state.accept.name
		}
	}

	return &Tables{
		StartState:  dfa.startID,
		Transitions: transitions,
		Actions:     actions,
		Rules:       stringifyRegexRules(regexRules, lexerRuleNames),
	}, nil
}

type ruleDef struct {
	name string
	expr *asts.ASTNode
}

func extractRuleDefs(ast *asts.AST) ([]ruleDef, error) {
	if ast == nil || ast.RootNode == nil {
		return nil, fmt.Errorf("nil AST")
	}
	if ast.RootNode.Type != parsers.EBNFParserNodeTypeGrammar {
		return nil, fmt.Errorf("expected grammar root, got %q", ast.RootNode.Type)
	}
	var rules []ruleDef
	for _, ruleNode := range ast.RootNode.Children {
		if ruleNode.Type != parsers.EBNFParserNodeTypeRule {
			return nil, fmt.Errorf("expected rule node, got %q", ruleNode.Type)
		}
		if err := ruleNode.CheckArity(2); err != nil {
			return nil, err
		}
		nameNode := ruleNode.Children[0]
		exprNode := ruleNode.Children[1]
		if nameNode.Type != parsers.EBNFParserNodeTypeIdentifier || nameNode.Token == nil {
			return nil, fmt.Errorf("rule name must be identifier")
		}
		ruleName := string(nameNode.Token.Lexeme)
		rules = append(rules, ruleDef{name: ruleName, expr: exprNode})
	}
	return rules, nil
}

func selectLexerRuleNames(ruleDefs []ruleDef) []string {
	var names []string
	for _, rule := range ruleDefs {
		if isLexerRuleName(rule.name) {
			names = append(names, rule.name)
		}
	}
	return names
}

func selectTokenRuleNames(ruleDefs []ruleDef) []string {
	var names []string
	for _, rule := range ruleDefs {
		if isTokenRuleName(rule.name) {
			names = append(names, rule.name)
		}
	}
	return names
}

func isLexerRuleName(name string) bool {
	if name == "" {
		return false
	}
	first := []rune(name)[0]
	if first == '!' {
		return true
	}
	return first == '_' || unicode.IsLower(first)
}

func isTokenRuleName(name string) bool {
	return isLexerRuleName(name) && !strings.HasPrefix(name, "_")
}

type regexKind int

const (
	regexLiteral regexKind = iota
	regexConcat
	regexAlternate
	regexOptional
	regexStar
	regexRange
)

type regexNode struct {
	kind     regexKind
	literal  string
	children []*regexNode
	from     rune
	to       rune
}

func buildRegexForRule(
	ruleName string,
	ruleMap map[string]*asts.ASTNode,
	lexerRuleSet map[string]bool,
	cache map[string]*regexNode,
	visiting map[string]bool,
) (*regexNode, error) {
	if node, ok := cache[ruleName]; ok {
		return node, nil
	}
	if visiting[ruleName] {
		return nil, fmt.Errorf("recursive rule expansion is not supported for lexer rules")
	}
	exprNode, ok := ruleMap[ruleName]
	if !ok {
		return nil, fmt.Errorf("undefined rule %q", ruleName)
	}
	visiting[ruleName] = true
	node, err := regexFromAST(exprNode, ruleMap, lexerRuleSet, cache, visiting)
	visiting[ruleName] = false
	if err != nil {
		return nil, err
	}
	cache[ruleName] = node
	return node, nil
}

func regexFromAST(
	node *asts.ASTNode,
	ruleMap map[string]*asts.ASTNode,
	lexerRuleSet map[string]bool,
	cache map[string]*regexNode,
	visiting map[string]bool,
) (*regexNode, error) {
	switch node.Type {
	case parsers.EBNFParserNodeTypeLiteral:
		if node.Token == nil {
			return nil, fmt.Errorf("literal node missing token")
		}
		text := string(node.Token.Lexeme)
		unquoted, err := strconv.Unquote(text)
		if err != nil {
			return nil, fmt.Errorf("invalid literal %q: %w", text, err)
		}
		return &regexNode{kind: regexLiteral, literal: unquoted}, nil
	case parsers.EBNFParserNodeTypeRange:
		if err := node.CheckArity(2); err != nil {
			return nil, err
		}
		startNode := node.Children[0]
		endNode := node.Children[1]
		if startNode.Type != parsers.EBNFParserNodeTypeLiteral || endNode.Type != parsers.EBNFParserNodeTypeLiteral {
			return nil, fmt.Errorf("range bounds must be string literals")
		}
		if startNode.Token == nil || endNode.Token == nil {
			return nil, fmt.Errorf("range bounds missing tokens")
		}
		startText := string(startNode.Token.Lexeme)
		endText := string(endNode.Token.Lexeme)
		startValue, err := strconv.Unquote(startText)
		if err != nil {
			return nil, fmt.Errorf("invalid range start %q: %w", startText, err)
		}
		endValue, err := strconv.Unquote(endText)
		if err != nil {
			return nil, fmt.Errorf("invalid range end %q: %w", endText, err)
		}
		startRunes := []rune(startValue)
		endRunes := []rune(endValue)
		if len(startRunes) != 1 || len(endRunes) != 1 {
			return nil, fmt.Errorf("range bounds must be single-rune string literals")
		}
		startRune := startRunes[0]
		endRune := endRunes[0]
		if startRune > endRune {
			return nil, fmt.Errorf("range start %q must be <= range end %q", startRune, endRune)
		}
		if startRune == endRune {
			return &regexNode{kind: regexLiteral, literal: string(startRune)}, nil
		}
		return &regexNode{kind: regexRange, from: startRune, to: endRune}, nil
	case parsers.EBNFParserNodeTypeWildcard:
		children := []*regexNode{
			{kind: regexRange, from: 0x0000, to: '\t'},
			{kind: regexRange, from: '\v', to: '\f'},
			{kind: regexRange, from: 0x000E, to: utf8.MaxRune},
		}
		return &regexNode{kind: regexAlternate, children: children}, nil
	case parsers.EBNFParserNodeTypeSequence:
		if len(node.Children) == 0 {
			return &regexNode{kind: regexLiteral, literal: ""}, nil
		}
		var children []*regexNode
		for _, child := range node.Children {
			part, err := regexFromAST(child, ruleMap, lexerRuleSet, cache, visiting)
			if err != nil {
				return nil, err
			}
			children = append(children, part)
		}
		if len(children) == 1 {
			return children[0], nil
		}
		return &regexNode{kind: regexConcat, children: children}, nil
	case parsers.EBNFParserNodeTypeAlternates:
		var children []*regexNode
		for _, child := range node.Children {
			part, err := regexFromAST(child, ruleMap, lexerRuleSet, cache, visiting)
			if err != nil {
				return nil, err
			}
			children = append(children, part)
		}
		if len(children) == 1 {
			return children[0], nil
		}
		return &regexNode{kind: regexAlternate, children: children}, nil
	case parsers.EBNFParserNodeTypeOptional:
		if err := node.CheckArity(1); err != nil {
			return nil, err
		}
		part, err := regexFromAST(node.Children[0], ruleMap, lexerRuleSet, cache, visiting)
		if err != nil {
			return nil, err
		}
		return &regexNode{kind: regexOptional, children: []*regexNode{part}}, nil
	case parsers.EBNFParserNodeTypeRepeat:
		if err := node.CheckArity(1); err != nil {
			return nil, err
		}
		part, err := regexFromAST(node.Children[0], ruleMap, lexerRuleSet, cache, visiting)
		if err != nil {
			return nil, err
		}
		return &regexNode{kind: regexStar, children: []*regexNode{part}}, nil
	case parsers.EBNFParserNodeTypeIdentifier:
		if node.Token == nil {
			return nil, fmt.Errorf("identifier node missing token")
		}
		identifier := string(node.Token.Lexeme)
		if !lexerRuleSet[identifier] {
			return nil, fmt.Errorf("identifier %q is not a lexer rule", identifier)
		}
		return buildRegexForRule(identifier, ruleMap, lexerRuleSet, cache, visiting)
	default:
		return nil, fmt.Errorf("unsupported node type %q", node.Type)
	}
}

func canBeEmpty(node *regexNode) bool {
	switch node.kind {
	case regexLiteral:
		return node.literal == ""
	case regexConcat:
		for _, child := range node.children {
			if !canBeEmpty(child) {
				return false
			}
		}
		return true
	case regexAlternate:
		for _, child := range node.children {
			if canBeEmpty(child) {
				return true
			}
		}
		return false
	case regexOptional, regexStar:
		return true
	case regexRange:
		return false
	default:
		return false
	}
}

type acceptRule struct {
	name     string
	priority int
}

type nfaState struct {
	id          int
	epsilon     []*nfaState
	transitions []nfaTransition
	accepts     []acceptRule
}

type nfaTransition struct {
	from rune
	to   rune
	next *nfaState
}

type nfaFragment struct {
	start   *nfaState
	accepts []*nfaState
}

type nfaBuilder struct {
	nextID int
}

func (builder *nfaBuilder) newState() *nfaState {
	state := &nfaState{
		id: builder.nextID,
	}
	builder.nextID++
	return state
}

func (builder *nfaBuilder) build(node *regexNode) (*nfaFragment, error) {
	switch node.kind {
	case regexLiteral:
		start := builder.newState()
		current := start
		for _, r := range []rune(node.literal) {
			next := builder.newState()
			current.transitions = append(current.transitions, nfaTransition{from: r, to: r, next: next})
			current = next
		}
		return &nfaFragment{start: start, accepts: []*nfaState{current}}, nil
	case regexRange:
		start := builder.newState()
		accept := builder.newState()
		start.transitions = append(start.transitions, nfaTransition{from: node.from, to: node.to, next: accept})
		return &nfaFragment{start: start, accepts: []*nfaState{accept}}, nil
	case regexConcat:
		if len(node.children) == 0 {
			start := builder.newState()
			return &nfaFragment{start: start, accepts: []*nfaState{start}}, nil
		}
		first, err := builder.build(node.children[0])
		if err != nil {
			return nil, err
		}
		current := first
		for _, child := range node.children[1:] {
			next, err := builder.build(child)
			if err != nil {
				return nil, err
			}
			for _, accept := range current.accepts {
				accept.epsilon = append(accept.epsilon, next.start)
			}
			current = &nfaFragment{start: first.start, accepts: next.accepts}
		}
		return current, nil
	case regexAlternate:
		start := builder.newState()
		accept := builder.newState()
		for _, child := range node.children {
			fragment, err := builder.build(child)
			if err != nil {
				return nil, err
			}
			start.epsilon = append(start.epsilon, fragment.start)
			for _, childAccept := range fragment.accepts {
				childAccept.epsilon = append(childAccept.epsilon, accept)
			}
		}
		return &nfaFragment{start: start, accepts: []*nfaState{accept}}, nil
	case regexOptional:
		if len(node.children) != 1 {
			return nil, fmt.Errorf("optional node must have one child")
		}
		start := builder.newState()
		accept := builder.newState()
		start.epsilon = append(start.epsilon, accept)
		fragment, err := builder.build(node.children[0])
		if err != nil {
			return nil, err
		}
		start.epsilon = append(start.epsilon, fragment.start)
		for _, childAccept := range fragment.accepts {
			childAccept.epsilon = append(childAccept.epsilon, accept)
		}
		return &nfaFragment{start: start, accepts: []*nfaState{accept}}, nil
	case regexStar:
		if len(node.children) != 1 {
			return nil, fmt.Errorf("star node must have one child")
		}
		start := builder.newState()
		accept := builder.newState()
		start.epsilon = append(start.epsilon, accept)
		fragment, err := builder.build(node.children[0])
		if err != nil {
			return nil, err
		}
		start.epsilon = append(start.epsilon, fragment.start)
		for _, childAccept := range fragment.accepts {
			childAccept.epsilon = append(childAccept.epsilon, fragment.start, accept)
		}
		return &nfaFragment{start: start, accepts: []*nfaState{accept}}, nil
	default:
		return nil, fmt.Errorf("unsupported regex node type")
	}
}

type dfaState struct {
	id          int
	nfaSet      map[int]*nfaState
	transitions []RangeTransition
	accept      *acceptRule
}

type dfaResult struct {
	startID int
	states  []*dfaState
}

func buildDFA(start *nfaState) *dfaResult {
	startSet := epsilonClosure(map[int]*nfaState{start.id: start})
	stateMap := map[string]*dfaState{}
	var states []*dfaState
	queue := []*dfaState{}
	nextID := 0

	newState := func(set map[int]*nfaState) *dfaState {
		state := &dfaState{
			id:          nextID,
			nfaSet:      set,
			transitions: nil,
			accept:      selectAcceptRule(set),
		}
		nextID++
		key := setKey(set)
		stateMap[key] = state
		states = append(states, state)
		queue = append(queue, state)
		return state
	}

	startState := newState(startSet)
	for len(queue) > 0 {
		state := queue[0]
		queue = queue[1:]
		ranges := buildDFATransitions(state.nfaSet)
		for _, tr := range ranges {
			targetSet := epsilonClosure(tr.targets)
			key := setKey(targetSet)
			targetState, ok := stateMap[key]
			if !ok {
				targetState = newState(targetSet)
			}
			state.transitions = append(state.transitions, RangeTransition{
				From: tr.from,
				To:   tr.to,
				Next: targetState.id,
			})
		}
	}

	return &dfaResult{startID: startState.id, states: states}
}

type dfaTransitionSeed struct {
	from    rune
	to      rune
	targets map[int]*nfaState
}

func buildDFATransitions(nfaSet map[int]*nfaState) []dfaTransitionSeed {
	var transitions []nfaTransition
	for _, nfa := range nfaSet {
		transitions = append(transitions, nfa.transitions...)
	}
	if len(transitions) == 0 {
		return nil
	}

	boundaries := make([]int, 0, len(transitions)*2)
	for _, tr := range transitions {
		boundaries = append(boundaries, int(tr.from))
		boundaries = append(boundaries, int(tr.to)+1)
	}
	sort.Ints(boundaries)
	unique := boundaries[:0]
	for _, b := range boundaries {
		if len(unique) == 0 || unique[len(unique)-1] != b {
			unique = append(unique, b)
		}
	}
	if len(unique) < 2 {
		return nil
	}

	var out []dfaTransitionSeed
	for i := 0; i < len(unique)-1; i++ {
		start := rune(unique[i])
		end := rune(unique[i+1] - 1)
		if start > end {
			continue
		}
		targetSet := map[int]*nfaState{}
		for _, tr := range transitions {
			if tr.from <= end && tr.to >= start {
				targetSet[tr.next.id] = tr.next
			}
		}
		if len(targetSet) == 0 {
			continue
		}
		out = append(out, dfaTransitionSeed{from: start, to: end, targets: targetSet})
	}
	return out
}

func epsilonClosure(initial map[int]*nfaState) map[int]*nfaState {
	out := map[int]*nfaState{}
	stack := make([]*nfaState, 0, len(initial))
	for _, state := range initial {
		out[state.id] = state
		stack = append(stack, state)
	}
	for len(stack) > 0 {
		state := stack[len(stack)-1]
		stack = stack[:len(stack)-1]
		for _, next := range state.epsilon {
			if _, ok := out[next.id]; ok {
				continue
			}
			out[next.id] = next
			stack = append(stack, next)
		}
	}
	return out
}

func setKey(set map[int]*nfaState) string {
	ids := make([]int, 0, len(set))
	for id := range set {
		ids = append(ids, id)
	}
	sort.Ints(ids)
	var b strings.Builder
	for i, id := range ids {
		if i > 0 {
			b.WriteByte(',')
		}
		fmt.Fprintf(&b, "%d", id)
	}
	return b.String()
}

func selectAcceptRule(set map[int]*nfaState) *acceptRule {
	var best *acceptRule
	for _, state := range set {
		for _, accept := range state.accepts {
			if best == nil || accept.priority < best.priority {
				candidate := accept
				best = &candidate
			}
		}
	}
	return best
}

func mergeRangeTransitions(transitions []RangeTransition) []RangeTransition {
	if len(transitions) == 0 {
		return nil
	}
	sort.Slice(transitions, func(i, j int) bool {
		if transitions[i].From == transitions[j].From {
			if transitions[i].To == transitions[j].To {
				return transitions[i].Next < transitions[j].Next
			}
			return transitions[i].To < transitions[j].To
		}
		return transitions[i].From < transitions[j].From
	})
	out := []RangeTransition{transitions[0]}
	for i := 1; i < len(transitions); i++ {
		prev := &out[len(out)-1]
		cur := transitions[i]
		if cur.From <= prev.To+1 && cur.Next == prev.Next {
			if cur.To > prev.To {
				prev.To = cur.To
			}
			continue
		}
		out = append(out, cur)
	}
	return out
}

func stringifyRegexRules(rules map[string]*regexNode, ruleOrder []string) map[string]string {
	if len(rules) == 0 {
		return nil
	}
	out := map[string]string{}
	for _, name := range ruleOrder {
		node, ok := rules[name]
		if !ok {
			continue
		}
		out[name] = regexToString(node)
	}
	return out
}

func regexToString(node *regexNode) string {
	switch node.kind {
	case regexLiteral:
		return strconv.Quote(node.literal)
	case regexConcat:
		var parts []string
		for _, child := range node.children {
			parts = append(parts, regexToString(child))
		}
		return strings.Join(parts, " ")
	case regexAlternate:
		var parts []string
		for _, child := range node.children {
			parts = append(parts, regexToString(child))
		}
		return "(" + strings.Join(parts, " | ") + ")"
	case regexOptional:
		return "(" + regexToString(node.children[0]) + ")?"
	case regexStar:
		return "(" + regexToString(node.children[0]) + ")*"
	case regexRange:
		return strconv.QuoteRuneToASCII(node.from) + "-" + strconv.QuoteRuneToASCII(node.to)
	default:
		return "<?>"
	}
}

// EncodeTables returns pretty-printed JSON for tables.
// opts may be nil; default is deterministic key order (Sort: true).
// When opts != nil and opts.Sort is false, encoding is faster but key order is nondeterministic.
func EncodeTables(tables *Tables, opts *EncodeOptions) ([]byte, error) {
	var b []byte
	var err error
	if opts != nil && !opts.Sort {
		type tablesAlias Tables
		b, err = json.Marshal((*tablesAlias)(tables))
	} else {
		b, err = marshalTablesDeterministic(tables)
	}
	if err != nil {
		return nil, err
	}
	var buf bytes.Buffer
	if err := json.Indent(&buf, b, "", "  "); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}
