package openai

import (
	"encoding/json"
	"strconv"
	"strings"
)

func parseMustacheToolCallArguments(raw string) (map[string]any, bool) {
	if args, ok := parseMustacheToolCallArgumentsStrict(raw); ok {
		return args, true
	}
	// Some Cline/provider outputs include one extra "}" before the closing
	// template parens, e.g. `...True}]})}}`. Recover only after strict parsing.
	trimmed := strings.TrimSpace(raw)
	if strings.HasSuffix(trimmed, "}") {
		return parseMustacheToolCallArgumentsStrict(strings.TrimSpace(trimmed[:len(trimmed)-1]))
	}
	return nil, false
}

func parseMustacheToolCallArgumentsStrict(raw string) (map[string]any, bool) {
	trimmed := strings.TrimSpace(raw)
	args := map[string]any{}
	if trimmed == "" {
		return args, true
	}
	for _, part := range splitTopLevel(trimmed, ',') {
		part = strings.TrimSpace(part)
		if part == "" {
			continue
		}
		eq := indexTopLevel(part, '=')
		if eq < 0 {
			return nil, false
		}
		key := strings.TrimSpace(part[:eq])
		if !isTextToolIdentifier(key) {
			return nil, false
		}
		value, ok := parseTextToolLiteral(part[eq+1:])
		if !ok {
			return nil, false
		}
		args[key] = value
	}
	return args, true
}

func parseTextToolLiteral(raw string) (any, bool) {
	trimmed := strings.TrimSpace(raw)
	if trimmed == "" {
		return "", true
	}
	var decoded any
	if err := json.Unmarshal([]byte(trimmed), &decoded); err == nil {
		return decoded, true
	}
	parser := pythonishLiteralParser{s: trimmed}
	value, ok := parser.parseValue()
	if !ok {
		return nil, false
	}
	parser.skipSpaces()
	if parser.pos != len(parser.s) {
		return nil, false
	}
	return value, true
}

func splitTopLevel(s string, sep byte) []string {
	parts := []string{}
	start := 0
	depth := 0
	var quote byte
	escaped := false
	for i := 0; i < len(s); i++ {
		c := s[i]
		if quote != 0 {
			if escaped {
				escaped = false
				continue
			}
			if c == '\\' {
				escaped = true
				continue
			}
			if c == quote {
				quote = 0
			}
			continue
		}
		switch c {
		case '\'', '"':
			quote = c
		case '[', '{', '(':
			depth++
		case ']', '}', ')':
			if depth > 0 {
				depth--
			}
		case sep:
			if depth == 0 {
				parts = append(parts, s[start:i])
				start = i + 1
			}
		}
	}
	parts = append(parts, s[start:])
	return parts
}

func indexTopLevel(s string, target byte) int {
	depth := 0
	var quote byte
	escaped := false
	for i := 0; i < len(s); i++ {
		c := s[i]
		if quote != 0 {
			if escaped {
				escaped = false
				continue
			}
			if c == '\\' {
				escaped = true
				continue
			}
			if c == quote {
				quote = 0
			}
			continue
		}
		switch c {
		case '\'', '"':
			quote = c
		case '[', '{', '(':
			depth++
		case ']', '}', ')':
			if depth > 0 {
				depth--
			}
		default:
			if c == target && depth == 0 {
				return i
			}
		}
	}
	return -1
}

func skipASCIISpaces(s string, pos int) int {
	for pos < len(s) {
		switch s[pos] {
		case ' ', '\n', '\r', '\t':
			pos++
		default:
			return pos
		}
	}
	return pos
}

type pythonishLiteralParser struct {
	s   string
	pos int
}

func (p *pythonishLiteralParser) parseValue() (any, bool) {
	p.skipSpaces()
	if p.pos >= len(p.s) {
		return nil, false
	}
	switch p.s[p.pos] {
	case '\'', '"':
		return p.parseString()
	case '[':
		return p.parseArray()
	case '{':
		return p.parseObject()
	}
	if p.consumeWord("True") || p.consumeWord("true") {
		return true, true
	}
	if p.consumeWord("False") || p.consumeWord("false") {
		return false, true
	}
	if p.consumeWord("None") || p.consumeWord("null") {
		return nil, true
	}
	return p.parseNumberOrBare()
}

func (p *pythonishLiteralParser) parseString() (string, bool) {
	if p.pos >= len(p.s) {
		return "", false
	}
	quote := p.s[p.pos]
	p.pos++
	var b strings.Builder
	for p.pos < len(p.s) {
		c := p.s[p.pos]
		p.pos++
		if c == quote {
			return b.String(), true
		}
		if c != '\\' {
			b.WriteByte(c)
			continue
		}
		if p.pos >= len(p.s) {
			return "", false
		}
		esc := p.s[p.pos]
		p.pos++
		switch esc {
		case '\\', '\'', '"':
			b.WriteByte(esc)
		case 'n':
			b.WriteByte('\n')
		case 'r':
			b.WriteByte('\r')
		case 't':
			b.WriteByte('\t')
		case 'b':
			b.WriteByte('\b')
		case 'f':
			b.WriteByte('\f')
		case 'u':
			if p.pos+4 > len(p.s) {
				return "", false
			}
			r, err := strconv.ParseInt(p.s[p.pos:p.pos+4], 16, 32)
			if err != nil {
				return "", false
			}
			b.WriteRune(rune(r))
			p.pos += 4
		default:
			b.WriteByte(esc)
		}
	}
	return "", false
}

func (p *pythonishLiteralParser) parseArray() ([]any, bool) {
	p.pos++
	items := []any{}
	p.skipSpaces()
	if p.consumeByte(']') {
		return items, true
	}
	for {
		value, ok := p.parseValue()
		if !ok {
			return nil, false
		}
		items = append(items, value)
		p.skipSpaces()
		if p.consumeByte(']') {
			return items, true
		}
		if !p.consumeByte(',') {
			return nil, false
		}
		p.skipSpaces()
		if p.consumeByte(']') {
			return items, true
		}
	}
}

func (p *pythonishLiteralParser) parseObject() (map[string]any, bool) {
	p.pos++
	obj := map[string]any{}
	p.skipSpaces()
	if p.consumeByte('}') {
		return obj, true
	}
	for {
		key, ok := p.parseObjectKey()
		if !ok {
			return nil, false
		}
		p.skipSpaces()
		if !p.consumeByte(':') {
			return nil, false
		}
		value, ok := p.parseValue()
		if !ok {
			return nil, false
		}
		obj[key] = value
		p.skipSpaces()
		if p.consumeByte('}') {
			return obj, true
		}
		if !p.consumeByte(',') {
			return nil, false
		}
		p.skipSpaces()
		if p.consumeByte('}') {
			return obj, true
		}
	}
}

func (p *pythonishLiteralParser) parseObjectKey() (string, bool) {
	p.skipSpaces()
	if p.pos >= len(p.s) {
		return "", false
	}
	if p.s[p.pos] == '\'' || p.s[p.pos] == '"' {
		return p.parseString()
	}
	start := p.pos
	for p.pos < len(p.s) && isTextToolIdentifierChar(p.s[p.pos], p.pos == start) {
		p.pos++
	}
	if start == p.pos {
		return "", false
	}
	return p.s[start:p.pos], true
}

func (p *pythonishLiteralParser) parseNumberOrBare() (any, bool) {
	start := p.pos
	for p.pos < len(p.s) && !isLiteralDelimiter(p.s[p.pos]) {
		p.pos++
	}
	if start == p.pos {
		return nil, false
	}
	token := p.s[start:p.pos]
	if value, ok := parseNumberToken(token); ok {
		return value, true
	}
	if isTextToolIdentifier(token) {
		return token, true
	}
	return nil, false
}

func (p *pythonishLiteralParser) skipSpaces() {
	for p.pos < len(p.s) {
		switch p.s[p.pos] {
		case ' ', '\n', '\r', '\t':
			p.pos++
		default:
			return
		}
	}
}

func (p *pythonishLiteralParser) consumeByte(b byte) bool {
	if p.pos < len(p.s) && p.s[p.pos] == b {
		p.pos++
		return true
	}
	return false
}

func (p *pythonishLiteralParser) consumeWord(word string) bool {
	if !strings.HasPrefix(p.s[p.pos:], word) {
		return false
	}
	end := p.pos + len(word)
	if end < len(p.s) && isBareLiteralChar(p.s[end]) {
		return false
	}
	p.pos = end
	return true
}

func parseNumberToken(token string) (any, bool) {
	if token == "" {
		return nil, false
	}
	if strings.ContainsAny(token, ".eE") {
		value, err := strconv.ParseFloat(token, 64)
		if err != nil {
			return nil, false
		}
		return value, true
	}
	value, err := strconv.ParseInt(token, 10, 64)
	if err != nil {
		return nil, false
	}
	return value, true
}

func isTextToolIdentifier(s string) bool {
	if s == "" {
		return false
	}
	for i := 0; i < len(s); i++ {
		if !isTextToolIdentifierChar(s[i], i == 0) {
			return false
		}
	}
	return true
}

func isTextToolIdentifierChar(c byte, first bool) bool {
	if c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_' {
		return true
	}
	if !first && (c >= '0' && c <= '9' || c == '.' || c == ':' || c == '-') {
		return true
	}
	return false
}

func isBareLiteralChar(c byte) bool {
	return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' || c == '_'
}

func isLiteralDelimiter(c byte) bool {
	switch c {
	case ',', ']', '}', ' ', '\n', '\r', '\t':
		return true
	default:
		return false
	}
}

func mapKeys(m map[string]any) []string {
	keys := make([]string, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	return keys
}
