package jsonpath

import (
	"regexp"
	"strconv"
	"unicode"
)

type state int

const (
	start state = iota
	unknown
	propOrIndex
	prop
	index
	indexEnd
	number
	doubleQuote
	doubleQuoteEscape
	singleQuote
	singleQuoteEscape
)

func Split(p string) ([]any, bool) {
	path := make([]any, 0)
	s := ""
	state := start
	for _, ch := range p {
		switch state {

		case start:
			switch {
			case ch == 'x':
				state = unknown
			case ch == '.':
				state = propOrIndex
			case ch == '[':
				state = index
			default:
				return path, false
			}

		case unknown:
			switch {
			case ch == '.':
				state = prop
				s = ""
			case ch == '[':
				state = index
				s = ""
			default:
				return path, false
			}

		case propOrIndex:
			switch {
			case isProp(ch):
				state = prop
				s = string(ch)
			case ch == '[':
				state = index
			default:
				return path, false
			}

		case prop:
			switch {
			case isProp(ch):
				s += string(ch)
			case ch == '.':
				state = prop
				path = append(path, s)
				s = ""
			case ch == '[':
				state = index
				path = append(path, s)
				s = ""
			default:
				return path, false
			}

		case index:
			switch {
			case unicode.IsDigit(ch):
				state = number
				s = string(ch)
			case ch == '"':
				state = doubleQuote
				s = ""
			case ch == '\'':
				state = singleQuote
				s = ""
			default:
				return path, false
			}

		case indexEnd:
			switch {
			case ch == ']':
				state = unknown
			default:
				return path, false
			}

		case number:
			switch {
			case unicode.IsDigit(ch):
				s += string(ch)
			case ch == ']':
				state = unknown
				n, err := strconv.Atoi(s)
				if err != nil {
					return path, false
				}
				path = append(path, n)
				s = ""
			default:
				return path, false
			}

		case doubleQuote:
			switch ch {
			case '"':
				state = indexEnd
				path = append(path, s)
				s = ""
			case '\\':
				state = doubleQuoteEscape
			default:
				s += string(ch)
			}

		case doubleQuoteEscape:
			switch ch {
			case '"':
				state = doubleQuote
				s += string(ch)
			default:
				return path, false
			}

		case singleQuote:
			switch ch {
			case '\'':
				state = indexEnd
				path = append(path, s)
				s = ""
			case '\\':
				state = singleQuoteEscape
				s += string(ch)
			default:
				s += string(ch)
			}

		case singleQuoteEscape:
			switch ch {
			case '\'':
				state = singleQuote
				s += string(ch)
			default:
				return path, false
			}
		}
	}
	if len(s) > 0 {
		if state == prop {
			path = append(path, s)
		} else {
			return path, false
		}

	}
	return path, true
}

func isProp(ch rune) bool {
	return unicode.IsLetter(ch) || unicode.IsDigit(ch) || ch == '_' || ch == '$'
}

var Identifier = regexp.MustCompile(`^[$a-zA-Z_][$a-zA-Z0-9_]*$`)

func Join(path []any) string {
	s := ""
	for _, v := range path {
		switch v := v.(type) {
		case string:
			if Identifier.MatchString(v) {
				s += "." + v
			} else {
				s += "[" + strconv.Quote(v) + "]"
			}
		case int:
			s += "[" + strconv.Itoa(v) + "]"
		}
	}
	return s
}
