package menuService

import (
	"sort"

	"git.ma-al.com/goc_daniel/b2b/app/model"
	"git.ma-al.com/goc_daniel/b2b/app/repos/categoriesRepo"
	routesRepo "git.ma-al.com/goc_daniel/b2b/app/repos/routesRepo"
	"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
)

type MenuService struct {
	categoriesRepo categoriesRepo.UICategoriesRepo
	routesRepo     routesRepo.UIRoutesRepo
}

func New() *MenuService {
	return &MenuService{
		categoriesRepo: categoriesRepo.New(),
		routesRepo:     routesRepo.New(),
	}
}

func (s *MenuService) GetMenu(id_lang uint) (*model.Category, error) {
	all_categories, err := s.categoriesRepo.GetAllCategories(id_lang)
	if err != nil {
		return &model.Category{}, err
	}

	// find the root
	root_index := 0
	root_found := false
	for i := 0; i < len(all_categories); i++ {
		if all_categories[i].IsRoot == 1 {
			root_index = i
			root_found = true
			break
		}
	}
	if !root_found {
		return &model.Category{}, responseErrors.ErrNoRootFound
	}

	// now create the children and reorder them according to position
	id_to_index := make(map[uint]int)
	for i := 0; i < len(all_categories); i++ {
		id_to_index[all_categories[i].CategoryID] = i
	}

	children_indices := make(map[int][]ChildWithPosition)
	for i := 0; i < len(all_categories); i++ {
		parent_index := id_to_index[all_categories[i].ParentID]
		children_indices[parent_index] = append(children_indices[parent_index], ChildWithPosition{Index: i, Position: all_categories[i].Position})
	}

	for key := range children_indices {
		sort.Sort(ByPosition(children_indices[key]))
	}

	// finally, create the tree
	tree := s.createTree(root_index, &all_categories, &children_indices)

	return &tree, nil
}

func (s *MenuService) createTree(index int, all_categories *([]model.ScannedCategory), children_indices *(map[int][]ChildWithPosition)) model.Category {
	node := s.scannedToNormalCategory((*all_categories)[index])

	for i := 0; i < len((*children_indices)[index]); i++ {
		node.Children = append(node.Children, s.createTree((*children_indices)[index][i].Index, all_categories, children_indices))
	}

	return node
}

func (s *MenuService) GetRoutes(id_lang uint) ([]model.Route, error) {
	return s.routesRepo.GetRoutes(id_lang)
}

func (s *MenuService) scannedToNormalCategory(scanned model.ScannedCategory) model.Category {
	var normal model.Category
	// normal.Active = scanned.Active
	normal.CategoryID = scanned.CategoryID
	normal.Label = scanned.Name
	// normal.Active = scanned.Active == 1
	normal.Params = model.CategpryParams{CategoryID: normal.CategoryID, LinkRewrite: scanned.LinkRewrite, Locale: scanned.IsoCode}
	normal.Children = []model.Category{}
	return normal
}

type ChildWithPosition struct {
	Index    int
	Position uint
}
type ByPosition []ChildWithPosition

func (a ByPosition) Len() int           { return len(a) }
func (a ByPosition) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByPosition) Less(i, j int) bool { return a[i].Position < a[j].Position }

func (s *MenuService) GetTopMenu(id uint) ([]*model.B2BTopMenu, error) {
	items, err := s.routesRepo.GetTopMenu(id)
	if err != nil {
		return nil, err
	}

	menuMap := make(map[int]*model.B2BTopMenu, len(items))
	roots := make([]*model.B2BTopMenu, 0)

	for i := range items {
		menu := &items[i]
		menu.Children = make([]*model.B2BTopMenu, 0)
		menuMap[menu.MenuID] = menu
	}

	for i := range items {
		menu := &items[i]

		if menu.ParentID == nil {
			roots = append(roots, menu)
			continue
		}

		parent, ok := menuMap[*menu.ParentID]
		if !ok {
			// fallback for orphaned nodes
			roots = append(roots, menu)
			continue
		}

		parent.Children = append(parent.Children, menu)
	}

	return roots, nil
}
