package parser

import (
	"eq2emu/internal/common"
	"fmt"
	"strconv"
	"strings"
	"sync"
)

var (
	fieldOrderPool = sync.Pool{
		New: func() any {
			slice := make([]string, 0, 32)
			return &slice
		},
	}
	conditionBuilder = sync.Pool{
		New: func() any {
			buf := make([]byte, 0, 128)
			return &buf
		},
	}
	stringBuilderPool = sync.Pool{
		New: func() any {
			return &stringBuilder{buf: make([]byte, 0, 64)}
		},
	}
)

type stringBuilder struct {
	buf []byte
}

func (sb *stringBuilder) reset() {
	sb.buf = sb.buf[:0]
}

func (sb *stringBuilder) writeString(s string) {
	sb.buf = append(sb.buf, s...)
}

func (sb *stringBuilder) string() string {
	return string(sb.buf)
}

// Parses PML into PacketDef structures
type Parser struct {
	lexer      *Lexer
	current    *Token
	input      string
	substructs map[string]*PacketDef
	templates  map[string]*PacketDef
	tagStack   []string
	fieldNames []string
}

// Type mapping using corrected type constants
var typeMap = map[string]common.EQ2DataType{
	"u8":     common.TypeInt8,
	"u16":    common.TypeInt16,
	"u32":    common.TypeInt32,
	"u64":    common.TypeInt64,
	"i8":     common.TypeSInt8,
	"i16":    common.TypeSInt16,
	"i32":    common.TypeSInt32,
	"i64":    common.TypeSInt64,
	"f32":    common.TypeFloat,
	"f64":    common.TypeDouble,
	"double": common.TypeDouble,
	"str8":   common.TypeString8,
	"str16":  common.TypeString16,
	"str32":  common.TypeString32,
	"char":   common.TypeChar,
	"color":  common.TypeColor,
	"equip":  common.TypeEquipment,
	"array":  common.TypeArray,
}

// Creates a new parser
func NewParser(input string) *Parser {
	parser := &Parser{
		lexer:      NewLexer(input),
		input:      input,
		substructs: make(map[string]*PacketDef),
		templates:  make(map[string]*PacketDef),
		tagStack:   make([]string, 0, 16),
		fieldNames: make([]string, 0, 8),
	}
	parser.advance()
	return parser
}

// Moves to next token with cleanup
func (p *Parser) advance() {
	if p.current != nil {
		p.lexer.ReleaseToken(p.current)
	}
	p.current = p.lexer.NextToken()

	// Skip comments
	for p.current.Type == TokenComment {
		p.lexer.ReleaseToken(p.current)
		p.current = p.lexer.NextToken()
	}
}

// Cleanup resources
func (p *Parser) cleanup() {
	if p.current != nil {
		p.lexer.ReleaseToken(p.current)
		p.current = nil
	}
}

// Pushes tag onto stack for validation
func (p *Parser) pushTag(tag string) {
	p.tagStack = append(p.tagStack, tag)
}

// Pops and validates closing tag
func (p *Parser) popTag(expectedTag string) error {
	if len(p.tagStack) == 0 {
		return fmt.Errorf("unexpected closing tag '%s' at line %d", expectedTag, p.current.Line)
	}

	lastTag := p.tagStack[len(p.tagStack)-1]
	if lastTag != expectedTag {
		return fmt.Errorf("mismatched closing tag: expected '%s', got '%s' at line %d", lastTag, expectedTag, p.current.Line)
	}

	p.tagStack = p.tagStack[:len(p.tagStack)-1]
	return nil
}

// Checks for unclosed tags
func (p *Parser) validateAllTagsClosed() error {
	if len(p.tagStack) > 0 {
		return fmt.Errorf("unclosed tag '%s'", p.tagStack[len(p.tagStack)-1])
	}
	return nil
}

// Fast type lookup using first character
func getDataType(tag string) (common.EQ2DataType, bool) {
	if len(tag) == 0 {
		return 0, false
	}

	switch tag[0] {
	case 'u':
		switch tag {
		case "u8":
			return common.TypeInt8, true
		case "u16":
			return common.TypeInt16, true
		case "u32":
			return common.TypeInt32, true
		case "u64":
			return common.TypeInt64, true
		}
	case 'i':
		switch tag {
		case "i8":
			return common.TypeSInt8, true
		case "i16":
			return common.TypeSInt16, true
		case "i32":
			return common.TypeSInt32, true
		case "i64":
			return common.TypeSInt64, true
		}
	case 's':
		switch tag {
		case "str8":
			return common.TypeString8, true
		case "str16":
			return common.TypeString16, true
		case "str32":
			return common.TypeString32, true
		}
	case 'f':
		switch tag {
		case "f32":
			return common.TypeFloat, true
		case "f64":
			return common.TypeDouble, true
		}
	case 'd':
		if tag == "double" {
			return common.TypeDouble, true
		}
	case 'c':
		switch tag {
		case "char":
			return common.TypeChar, true
		case "color":
			return common.TypeColor, true
		}
	case 'e':
		if tag == "equip" {
			return common.TypeEquipment, true
		}
	case 'a':
		if tag == "array" {
			return common.TypeArray, true
		}
	}

	// Fallback to map lookup
	if dataType, exists := typeMap[tag]; exists {
		return dataType, true
	}

	return 0, false
}

// Parses field names with minimal allocations
func (p *Parser) parseFieldNames(nameAttr string) []string {
	if nameAttr == "" {
		return nil
	}

	// Fast path for single name
	if strings.IndexByte(nameAttr, ',') == -1 {
		name := strings.TrimSpace(nameAttr)
		if name != "" {
			p.fieldNames = p.fieldNames[:0]
			p.fieldNames = append(p.fieldNames, name)
			return p.fieldNames
		}
		return nil
	}

	// Parse multiple names efficiently
	p.fieldNames = p.fieldNames[:0]
	start := 0
	for i := 0; i < len(nameAttr); i++ {
		if nameAttr[i] == ',' {
			if name := strings.TrimSpace(nameAttr[start:i]); name != "" {
				p.fieldNames = append(p.fieldNames, name)
			}
			start = i + 1
		}
	}
	if name := strings.TrimSpace(nameAttr[start:]); name != "" {
		p.fieldNames = append(p.fieldNames, name)
	}

	return p.fieldNames
}

// Builds field name with prefix efficiently
func buildFieldName(prefix, name string) string {
	if prefix == "" {
		return name
	}

	sb := stringBuilderPool.Get().(*stringBuilder)
	sb.reset()
	defer stringBuilderPool.Put(sb)

	sb.writeString(prefix)
	sb.writeString(name)
	return sb.string()
}

// Combines conditions with AND logic
func combineConditions(cond1, cond2 string) string {
	if cond1 == "" {
		return cond2
	}
	if cond2 == "" {
		return cond1
	}

	bufPtr := conditionBuilder.Get().(*[]byte)
	buf := *bufPtr
	buf = buf[:0]
	defer conditionBuilder.Put(bufPtr)

	buf = append(buf, cond1...)
	buf = append(buf, '&')
	buf = append(buf, cond2...)
	*bufPtr = buf

	return string(buf)
}

// Parses the entire PML document
func (p *Parser) Parse() (map[string]*PacketDef, error) {
	packets := make(map[string]*PacketDef)

	for p.current.Type != TokenEOF {
		if p.current.Type == TokenError {
			return nil, fmt.Errorf("parse error at line %d, col %d", p.current.Line, p.current.Col)
		}

		if p.current.Type == TokenOpenTag || p.current.Type == TokenSelfCloseTag {
			switch p.current.Tag(p.input) {
			case "packet":
				name := p.current.Attributes["name"]
				packet, err := p.parsePacket()
				if err != nil {
					return nil, err
				}
				if name != "" {
					packets[name] = packet
				}
			case "substruct":
				name := p.current.Attributes["name"]
				substruct, err := p.parseSubstruct()
				if err != nil {
					return nil, err
				}
				if name != "" {
					p.substructs[name] = substruct
				}
			case "template":
				name := p.current.Attributes["name"]
				if name != "" {
					err := p.parseTemplateDefinition(name)
					if err != nil {
						return nil, err
					}
				} else {
					p.advance()
				}
			default:
				p.advance()
			}
		} else {
			p.advance()
		}
	}

	if err := p.validateAllTagsClosed(); err != nil {
		return nil, err
	}

	return packets, nil
}

// Parses a packet element with version children
func (p *Parser) parsePacket() (*PacketDef, error) {
	packetDef := NewPacketDef(16)

	if p.current.Type == TokenSelfCloseTag {
		p.advance()
		return packetDef, nil
	}

	p.pushTag("packet")
	p.advance()

	for p.current.Type != TokenEOF && !(p.current.Type == TokenCloseTag && p.current.Tag(p.input) == "packet") {
		if p.current.Type == TokenOpenTag && p.current.Tag(p.input) == "version" {
			err := p.parseVersion(packetDef)
			if err != nil {
				return nil, err
			}
		} else {
			p.advance()
		}
	}

	if p.current.Type == TokenCloseTag && p.current.Tag(p.input) == "packet" {
		if err := p.popTag("packet"); err != nil {
			return nil, err
		}
		p.advance()
	} else {
		return nil, fmt.Errorf("expected closing tag for packet at line %d", p.current.Line)
	}

	return packetDef, nil
}

// Parses a version element
func (p *Parser) parseVersion(packetDef *PacketDef) error {
	attrs := p.current.Attributes
	version := uint32(1)
	if v := attrs["number"]; v != "" {
		if parsed, err := strconv.ParseUint(v, 10, 32); err == nil {
			version = uint32(parsed)
		}
	}

	fieldOrder := fieldOrderPool.Get().(*[]string)
	*fieldOrder = (*fieldOrder)[:0]
	defer fieldOrderPool.Put(fieldOrder)

	if p.current.Type == TokenSelfCloseTag {
		p.advance()
		packetDef.Orders[version] = make([]string, len(*fieldOrder))
		copy(packetDef.Orders[version], *fieldOrder)
		return nil
	}

	p.pushTag("version")
	p.advance()

	err := p.parseElements(packetDef, fieldOrder, "")
	if err != nil {
		return err
	}

	if p.current.Type == TokenCloseTag && p.current.Tag(p.input) == "version" {
		if err := p.popTag("version"); err != nil {
			return err
		}
		p.advance()
	} else {
		return fmt.Errorf("expected closing tag for version at line %d", p.current.Line)
	}

	packetDef.Orders[version] = make([]string, len(*fieldOrder))
	copy(packetDef.Orders[version], *fieldOrder)
	return nil
}

// Parses a substruct element
func (p *Parser) parseSubstruct() (*PacketDef, error) {
	packetDef := NewPacketDef(16)

	fieldOrder := fieldOrderPool.Get().(*[]string)
	*fieldOrder = (*fieldOrder)[:0]
	defer fieldOrderPool.Put(fieldOrder)

	if p.current.Type == TokenSelfCloseTag {
		p.advance()
		packetDef.Orders[1] = make([]string, len(*fieldOrder))
		copy(packetDef.Orders[1], *fieldOrder)
		return packetDef, nil
	}

	p.pushTag("substruct")
	p.advance()

	err := p.parseElements(packetDef, fieldOrder, "")
	if err != nil {
		return nil, err
	}

	if p.current.Type == TokenCloseTag && p.current.Tag(p.input) == "substruct" {
		if err := p.popTag("substruct"); err != nil {
			return nil, err
		}
		p.advance()
	} else {
		return nil, fmt.Errorf("expected closing tag for substruct")
	}

	packetDef.Orders[1] = make([]string, len(*fieldOrder))
	copy(packetDef.Orders[1], *fieldOrder)
	return packetDef, nil
}

// Processes child elements
func (p *Parser) parseElements(packetDef *PacketDef, fieldOrder *[]string, prefix string) error {
	for p.current.Type != TokenEOF && !(p.current.Type == TokenCloseTag) {
		switch p.current.Type {
		case TokenOpenTag, TokenSelfCloseTag:
			switch p.current.Tag(p.input) {
			case "group":
				err := p.parseGroup(packetDef, fieldOrder, prefix)
				if err != nil {
					return err
				}
			case "array":
				err := p.parseArray(packetDef, fieldOrder, prefix)
				if err != nil {
					return err
				}
			case "template":
				err := p.parseTemplate(packetDef, fieldOrder, prefix)
				if err != nil {
					return err
				}
			default:
				err := p.parseField(packetDef, fieldOrder, prefix)
				if err != nil {
					return err
				}
			}
		case TokenText:
			p.advance()
		default:
			p.advance()
		}
	}
	return nil
}

// Handles template definition and usage
func (p *Parser) parseTemplate(packetDef *PacketDef, fieldOrder *[]string, prefix string) error {
	attrs := p.current.Attributes

	// Template definition: <template name="foo">
	if templateName := attrs["name"]; templateName != "" {
		return p.parseTemplateDefinition(templateName)
	}

	// Template usage: <template use="foo">
	if templateUse := attrs["use"]; templateUse != "" {
		return p.parseTemplateUsage(packetDef, fieldOrder, prefix, templateUse)
	}

	// No name or use attribute - skip
	p.advance()
	return nil
}

// Parses template definition and stores it
func (p *Parser) parseTemplateDefinition(templateName string) error {
	templateDef := NewPacketDef(16)
	fieldOrder := fieldOrderPool.Get().(*[]string)
	*fieldOrder = (*fieldOrder)[:0]
	defer fieldOrderPool.Put(fieldOrder)

	if p.current.Type == TokenSelfCloseTag {
		p.advance()
		templateDef.Orders[1] = make([]string, len(*fieldOrder))
		copy(templateDef.Orders[1], *fieldOrder)
		p.templates[templateName] = templateDef
		return nil
	}

	p.pushTag("template")
	p.advance()

	err := p.parseElements(templateDef, fieldOrder, "")
	if err != nil {
		return err
	}

	if p.current.Type == TokenCloseTag && p.current.Tag(p.input) == "template" {
		if err := p.popTag("template"); err != nil {
			return err
		}
		p.advance()
	} else {
		return fmt.Errorf("expected closing tag for template at line %d", p.current.Line)
	}

	templateDef.Orders[1] = make([]string, len(*fieldOrder))
	copy(templateDef.Orders[1], *fieldOrder)
	p.templates[templateName] = templateDef
	return nil
}

// Injects template fields into current parsing context
func (p *Parser) parseTemplateUsage(packetDef *PacketDef, fieldOrder *[]string, prefix string, templateName string) error {
	template, exists := p.templates[templateName]
	if !exists {
		return fmt.Errorf("undefined template '%s' at line %d", templateName, p.current.Line)
	}

	// Inject all template fields into current packet
	if templateOrder, exists := template.Orders[1]; exists {
		for _, fieldName := range templateOrder {
			if fieldDesc, exists := template.Fields[fieldName]; exists {
				// Apply prefix if provided
				fullName := fieldName
				if prefix != "" {
					fullName = buildFieldName(prefix, fieldName)
				}

				packetDef.Fields[fullName] = fieldDesc
				*fieldOrder = append(*fieldOrder, fullName)
			}
		}
	}

	p.advance()
	return nil
}

// Handles group elements
func (p *Parser) parseGroup(packetDef *PacketDef, fieldOrder *[]string, prefix string) error {
	attrs := p.current.Attributes
	groupPrefix := prefix
	if name := attrs["name"]; name != "" {
		if prefix == "" {
			groupPrefix = name + "_"
		} else {
			groupPrefix = prefix + name + "_"
		}
	}

	if p.current.Type == TokenSelfCloseTag {
		p.advance()
		return nil
	}

	p.pushTag("group")
	p.advance()

	err := p.parseElements(packetDef, fieldOrder, groupPrefix)
	if err != nil {
		return err
	}

	if p.current.Type == TokenCloseTag && p.current.Tag(p.input) == "group" {
		if err := p.popTag("group"); err != nil {
			return err
		}
		p.advance()
	} else {
		return fmt.Errorf("expected closing tag for group at line %d", p.current.Line)
	}

	return nil
}

// Handles array elements
func (p *Parser) parseArray(packetDef *PacketDef, fieldOrder *[]string, prefix string) error {
	attrs := p.current.Attributes

	var arrayName string
	if prefix == "" {
		arrayName = attrs["name"]
	} else {
		arrayName = buildFieldName(prefix, attrs["name"])
	}

	fieldDesc := FieldDesc{
		Type:        common.TypeArray,
		Condition:   attrs["count"],
		AddToStruct: true, // Default to true
	}

	if ifCond := attrs["if"]; ifCond != "" {
		fieldDesc.Condition = combineConditions(fieldDesc.Condition, ifCond)
	}

	// Parse additional attributes
	if maxSize := attrs["max_size"]; maxSize != "" {
		if m, err := strconv.Atoi(maxSize); err == nil {
			fieldDesc.MaxArraySize = m
		} else {
			return fmt.Errorf("invalid max_size value '%s' at line %d: %v", maxSize, p.current.Line, err)
		}
	}

	if optional := attrs["optional"]; optional == "true" {
		fieldDesc.Optional = true
	}

	if addToStruct := attrs["add_to_struct"]; addToStruct == "false" {
		fieldDesc.AddToStruct = false
	}

	// Handle substruct reference
	if substruct := attrs["substruct"]; substruct != "" {
		if subDef, exists := p.substructs[substruct]; exists {
			fieldDesc.SubDef = subDef
		}
	}

	// Arrays with substruct references or explicit self-closing syntax are self-closing
	if p.current.Type == TokenSelfCloseTag || fieldDesc.SubDef != nil {
		p.advance()
		packetDef.Fields[arrayName] = fieldDesc
		*fieldOrder = append(*fieldOrder, arrayName)
		return nil
	}

	p.pushTag("array")
	p.advance()

	// Handle direct child elements as substruct fields (no wrapper needed)
	if fieldDesc.SubDef == nil {
		subDef := NewPacketDef(16)
		subOrder := fieldOrderPool.Get().(*[]string)
		*subOrder = (*subOrder)[:0]
		defer fieldOrderPool.Put(subOrder)

		err := p.parseElements(subDef, subOrder, "")
		if err != nil {
			return err
		}

		// Only create substruct if we actually have fields
		if len(*subOrder) > 0 {
			subDef.Orders[1] = make([]string, len(*subOrder))
			copy(subDef.Orders[1], *subOrder)
			fieldDesc.SubDef = subDef
		}
	}

	if p.current.Type == TokenCloseTag && p.current.Tag(p.input) == "array" {
		if err := p.popTag("array"); err != nil {
			return err
		}
		p.advance()
	} else {
		return fmt.Errorf("expected closing tag for array at line %d", p.current.Line)
	}

	packetDef.Fields[arrayName] = fieldDesc
	*fieldOrder = append(*fieldOrder, arrayName)
	return nil
}

// Handles field elements
func (p *Parser) parseField(packetDef *PacketDef, fieldOrder *[]string, prefix string) error {
	tagName := p.current.Tag(p.input)
	attrs := p.current.Attributes

	dataType, exists := getDataType(tagName)
	if !exists {
		p.advance()
		return nil
	}

	nameAttr := attrs["name"]
	if nameAttr == "" {
		p.advance()
		return nil
	}

	names := p.parseFieldNames(nameAttr)
	for _, name := range names {
		var fullName string
		if prefix == "" {
			fullName = name
		} else {
			fullName = buildFieldName(prefix, name)
		}

		fieldDesc := FieldDesc{
			Type:        dataType,
			Condition:   attrs["if"],
			AddToStruct: true, // Default to true
			AddType:     dataType,
		}

		// Parse size attribute
		if size := attrs["size"]; size != "" {
			if s, err := strconv.Atoi(size); err == nil {
				fieldDesc.Length = s
			} else {
				return fmt.Errorf("invalid size value '%s' at line %d: %v", size, p.current.Line, err)
			}
		}

		// Parse oversized attribute
		if oversized := attrs["oversized"]; oversized != "" {
			if o, err := strconv.Atoi(oversized); err == nil {
				fieldDesc.Oversized = o
			} else {
				return fmt.Errorf("invalid oversized value '%s' at line %d: %v", oversized, p.current.Line, err)
			}
		}

		// Parse type2 attributes
		if type2 := attrs["type2"]; type2 != "" {
			if t2, exists := getDataType(type2); exists {
				fieldDesc.Type2 = t2
				fieldDesc.Type2Cond = attrs["type2_if"]
			}
		}

		// Parse default value
		if defaultVal := attrs["default"]; defaultVal != "" {
			if d, err := strconv.ParseInt(defaultVal, 10, 8); err == nil {
				fieldDesc.DefaultValue = int8(d)
			} else {
				return fmt.Errorf("invalid default value '%s' at line %d: %v", defaultVal, p.current.Line, err)
			}
		}

		// Parse max_size
		if maxSize := attrs["max_size"]; maxSize != "" {
			if m, err := strconv.Atoi(maxSize); err == nil {
				fieldDesc.MaxArraySize = m
			} else {
				return fmt.Errorf("invalid max_size value '%s' at line %d: %v", maxSize, p.current.Line, err)
			}
		}

		// Parse optional
		if optional := attrs["optional"]; optional == "true" {
			fieldDesc.Optional = true
		}

		// Parse add_to_struct
		if addToStruct := attrs["add_to_struct"]; addToStruct == "false" {
			fieldDesc.AddToStruct = false
		}

		// Parse add_type
		if addType := attrs["add_type"]; addType != "" {
			if at, exists := getDataType(addType); exists {
				fieldDesc.AddType = at
			}
		}

		packetDef.Fields[fullName] = fieldDesc
		*fieldOrder = append(*fieldOrder, fullName)
	}

	p.advance()
	return nil
}

// Parses PML content and returns PacketDef map
func Parse(content string) (map[string]*PacketDef, error) {
	parser := NewParser(content)
	defer parser.cleanup()
	return parser.Parse()
}
