// Package  utils add by blacknc <blacknc@163.com> at 2024/04/15 17:32:56
package utils

import (
	"dataservices/commons/errs"
	"encoding/json"
	"fmt"
	"strings"
)

type PathTrieNode struct {
	Commchildren map[string]*PathTrieNode `json:"commchildren"`
	Wildchild    *PathTrieNode            `json:"wildchild"`
	Name         string                   `json:"name"`
	EndOfPath    bool                     `json:"end_of_path"`
	Fullpath     string                   `json:"fullpath"`
	Data         any                      `json:"data"`
}

type PathTrie struct {
	RootMap map[string]*PathTrieNode `json:"root_map"`
}

func (t *PathTrie) Marshal() ([]byte, error) {
	return json.Marshal(t)
}
func (t *PathTrie) Unmarshal(data []byte) error {
	return json.Unmarshal(data, t)
}

func NewPathTrie() *PathTrie {
	return &PathTrie{
		RootMap: make(map[string]*PathTrieNode),
	}
}

func (t *PathTrie) getRoot(method string) *PathTrieNode {
	method = strings.ToUpper(method)
	root, ok := t.RootMap[method]
	if !ok {
		t.RootMap[method] = &PathTrieNode{
			Commchildren: make(map[string]*PathTrieNode),
		}
		root = t.RootMap[method]
	}
	return root
}

func (t *PathTrie) Insert(method, rawpath string, data any) error {
	root := t.getRoot(method)
	path := strings.TrimLeft(rawpath, "/")
	for _, segment := range strings.Split(path, "/") {
		if len(segment) > 0 && segment[0] == '{' {
			n := len(segment)
			if n <= 2 || segment[n-1] != '}' {
				return errs.NewBadRequest(fmt.Sprintf("invalid wildcard path segment, path:%s, segment:%s", path, segment), nil)
			}
			if root.Wildchild == nil {
				root.Wildchild = &PathTrieNode{
					Commchildren: make(map[string]*PathTrieNode),
					Fullpath:     root.Fullpath + "/" + segment,
					Name:         segment[1 : n-1],
				}
			} else if root.Wildchild.Name != segment[1:n-1] {
				return errs.NewBadRequest(fmt.Sprintf("'%s' in new path '%s' conflicts with existing wildcard '{%s}' in existing prefix '%s/{%s}'",
					segment, path, root.Wildchild.Name, root.Fullpath, root.Wildchild.Name), nil)
			}
			root = root.Wildchild
		} else {
			if _, ok := root.Commchildren[segment]; !ok {
				root.Commchildren[segment] = &PathTrieNode{
					Commchildren: make(map[string]*PathTrieNode),
					Fullpath:     root.Fullpath + "/" + segment,
					Name:         segment,
				}
			}
			root = root.Commchildren[segment]
		}
	}
	if root.EndOfPath {
		return errs.NewBadRequest("Endpoint with "+method+":"+rawpath+" already exists under the dataapp", nil)
	}
	root.EndOfPath = true
	root.Data = data
	return nil
}

func (t *PathTrie) Search(method, path string) (bool, any, map[string]string) {
	root := t.getRoot(method)
	path = strings.TrimLeft(path, "/")
	params := make(map[string]string)
	for _, segment := range strings.Split(path, "/") {
		if child, ok := root.Commchildren[segment]; ok {
			root = child
		} else if root.Wildchild != nil {
			params[root.Wildchild.Name] = segment
			root = root.Wildchild
		} else {
			return false, nil, nil
		}
	}
	if root.EndOfPath {
		return true, root.Data, params
	}
	return false, nil, nil
}
