package orm

import (
	"go/scanner"
	"go/token"
	"strings"
	"unicode"

	"go.mongodb.org/mongo-driver/v2/mongo"
)

var optionKeywords = [...]string{"unique", "sparse", "background", "dropdups"}

// prolly won't need to use indexes, but just in case...
type InternalIndex struct {
	Fields       []string
	Options      []string
	lastFieldIdx int
	sticky       bool
}

func (in *InternalIndex) appendOption(o string) {
	o = strings.ToLower(o)
	o = strings.Trim(o, " ")
	for i := range optionKeywords {
		if optionKeywords[i] == o {
			in.Options = append(in.Options, o)
		}
	}
}

func (in *InternalIndex) appendField(f string) {
	in.Fields = append(in.Fields, f)
}

func (in *InternalIndex) appendDotField(f string) {
	in.Fields[in.lastFieldIdx] = in.Fields[in.lastFieldIdx] + "." + f
}

func (in *InternalIndex) updateLastField() {
	if len(in.Fields) > 0 {
		in.lastFieldIdx = len(in.Fields) - 1
		return
	}
	in.lastFieldIdx = 0
}

func (in *InternalIndex) setSticky(s bool) {
	in.sticky = s
}

func (in *InternalIndex) getSticky() bool {
	return in.sticky
}

func scanIndex(src string) []InternalIndex {
	var s scanner.Scanner
	var parsed []InternalIndex

	src = func(ss string) string {
		return strings.Map(func(r rune) rune {
			if unicode.IsSpace(r) {
				return -1
			}
			return r
		}, ss)
	}(src)
	fset := token.NewFileSet()
	file := fset.AddFile("", fset.Base(), len(src))
	s.Init(file, []byte(src), nil, scanner.ScanComments)

	lb := false

	p := &InternalIndex{}

	for {
		_, tok, lit := s.Scan()

		switch tok {
		case token.LBRACE:
			if lb {
				goto _panik
			}
			lb = true
		case token.RBRACE:
			if !lb || len(p.Fields) == 0 {
				goto _panik
			}
			lb = false
		case token.IDENT:
			if lb {
				if p.getSticky() {
					p.appendDotField(lit)
					p.setSticky(false)
					break
				}
				p.appendField(lit)
				break
			}
			p.appendOption(lit)
		case token.PERIOD:
			if p.getSticky() {
				goto _panik
			}
			p.setSticky(true)
			p.updateLastField()
		case token.COMMA:
		case token.COLON:
			if lb {
				goto _panik
			}
		case token.SEMICOLON:
			if lb {
				goto _panik
			}
			parsed = append(parsed, *p)
			p = &InternalIndex{}
		case token.EOF:
			if lb {
				goto _panik
			}
			return parsed
		default:
			goto _panik
		}
	}

_panik:
	panic("parsing error in index expression!")
}

func buildIndex(i InternalIndex) *mongo.IndexModel {
	idx := &mongo.IndexModel{
		Keys: i.Fields,
	}

	for _, o := range i.Options {
		switch o {
		case "unique":
			idx.Options.SetUnique(true)
		//case "background":
		//	idx.Options.SetBackground(true)
		case "sparse":
			idx.Options.SetSparse(true)
		}
	}
	return idx
}