// Package trie handles the generation of compressed tries for character properties
package main

import (
	"bytes"
	"fmt"
	"go/format"
	"io"
	"os"
	"unicode"

	"github.com/clipperhouse/displaywidth/internal/gen/triegen"
)

// GenerateTrie creates a compressed trie from Unicode data using triegen
func GenerateTrie(data *UnicodeData) (*triegen.Trie, error) {
	trie := triegen.NewTrie("stringWidth")

	// Insert all characters with non-default properties
	inserted := 0
	for r := rune(0); r <= unicode.MaxRune; r++ {
		// Skip surrogate characters (U+D800-U+DFFF) and other invalid characters
		if r >= 0xD800 && r <= 0xDFFF {
			continue
		}

		// Skip characters that would create invalid UTF-8
		if r == unicode.ReplacementChar {
			continue
		}

		props := buildPropertyBitmap(r, data)

		// Only insert characters with non-default properties
		if props != 0 {
			trie.Insert(r, uint64(props))
			inserted++
		}
	}

	fmt.Printf("Inserted %d characters with non-default properties\n", inserted)
	return trie, nil
}

// WriteTrieGo generates the Go code for the trie using triegen
func WriteTrieGo(trie *triegen.Trie, outputPath string) error {
	buf := &bytes.Buffer{}

	// Write package header
	fmt.Fprintf(buf, "// Code generated by internal/gen/main.go. DO NOT EDIT.\n\n")
	fmt.Fprintf(buf, "package displaywidth\n\n")

	// Write property definitions
	writeProperties(buf)

	// Generate the trie using triegen (it will use uint8/uint16/etc directly)
	size, err := trie.Gen(buf)
	if err != nil {
		return fmt.Errorf("failed to generate trie: %v", err)
	}

	b := buf.Bytes()
	typename := "stringWidthTrie"

	typeDefSig := `type ` + typename + ` struct`
	noTypeDef := `// ` + typeDefSig
	b = bytes.ReplaceAll(b, []byte(typeDefSig), []byte(noTypeDef))

	lookupSig := `(t *` + typename + `) lookup(s []byte)`
	genericLookupSig := `lookup[T ~string | []byte](s T)`
	b = bytes.ReplaceAll(b, []byte(lookupSig), []byte(genericLookupSig))

	lookupValueSig := `(t *` + typename + `) lookupValue`
	genericLookupValueSig := `lookupValue`
	b = bytes.ReplaceAll(b, []byte(lookupValueSig), []byte(genericLookupValueSig))

	lookupCallSig := `t.lookupValue(`
	genericLookupCallSig := `lookupValue(`
	b = bytes.ReplaceAll(b, []byte(lookupCallSig), []byte(genericLookupCallSig))

	formatted, err := format.Source(b)
	if err != nil {
		return err
	}

	dst, err := os.Create(outputPath)
	if err != nil {
		return err
	}
	defer dst.Close()

	_, err = dst.Write(formatted)
	if err != nil {
		return err
	}

	fmt.Printf("Generated trie with size %d bytes\n", size)
	return nil
}

// writeProperties writes the character properties definitions to the buffer.
// It uses PropertyDefinitions from unicode.go as the single source of truth.
func writeProperties(w io.Writer) {
	fmt.Fprintf(w, "// property is an enum representing the properties of a character\n")
	fmt.Fprintf(w, "type property uint8\n\n")
	fmt.Fprintf(w, "const (\n")

	for i, prop := range PropertyDefinitions {
		fmt.Fprintf(w, "// %s\n", prop.Comment)

		constName := "_" + prop.Name
		if i == 0 {
			fmt.Fprintf(w, "%s property = iota + 1\n", constName)
		} else {
			fmt.Fprintf(w, "%s\n", constName)
		}
	}

	fmt.Fprintf(w, ")\n\n")
}
