package biz

import (
	"context"
	"time"
)

type MenuButton struct {
	CreatedAt   time.Time
	UpdatedAt   time.Time
	DeletedAt   *time.Time
	ID          uint
	Name        string
	Description string
	MenuID      uint
	AuthorityID uint
}

type MenuParameter struct {
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt *time.Time
	ID        uint
	MenuID    uint
	Type      string
	Key       string
	Value     string
}

type Menu struct {
	CreatedAt      time.Time
	UpdatedAt      time.Time
	DeletedAt      *time.Time
	ID             uint
	MenuID         uint
	ParentID       uint
	Path           string
	Name           string
	Hidden         bool
	Component      string
	Sort           int
	ActiveName     string
	KeepAlive      bool
	DefaultMenu    bool
	Title          string
	Icon           string
	CloseTab       bool
	TransitionType string
	Children       []*Menu
	Buttons        []*MenuButton
	Parameters     []*MenuParameter
}

type MenuRepo interface {
	Create(context.Context, *Menu) error
	Update(context.Context, *Menu) error
	Delete(context.Context, uint) error
	Find(context.Context, uint) (*Menu, error)
	List(context.Context) ([]*Menu, error)
	SetAuthorityMenus(context.Context, uint, []uint) error
	AuthorityMenuIDs(context.Context, uint) ([]uint, error)
	// ListAuthorityMenus returns the base menus assigned to the target
	// authority. Unlike List, it must not apply the current actor's strict
	// menu filter: the administration endpoint is explicitly querying another
	// role's assignments.
	ListAuthorityMenus(context.Context, uint) ([]*Menu, error)
	MenuRoleIDs(context.Context, uint) ([]uint, error)
	DefaultRouterRoleIDs(context.Context, uint) ([]uint, error)
	SetMenuRoles(context.Context, uint, []uint) error
}

type MenuUsecase struct{ repo MenuRepo }

func NewMenuUsecase(repo MenuRepo) *MenuUsecase { return &MenuUsecase{repo: repo} }

func (uc *MenuUsecase) List(ctx context.Context) ([]*Menu, error) {
	return uc.repo.List(ctx)
}

func (uc *MenuUsecase) Tree(ctx context.Context) ([]*Menu, error) {
	items, err := uc.repo.List(ctx)
	if err != nil {
		return nil, err
	}
	byID := make(map[uint]*Menu, len(items))
	for _, item := range items {
		item.Children = nil
		byID[item.ID] = item
	}
	var roots []*Menu
	for _, item := range items {
		if parent := byID[item.ParentID]; parent != nil {
			parent.Children = append(parent.Children, item)
		} else if item.ParentID == 0 {
			roots = append(roots, item)
		}
	}
	return roots, nil
}

func (uc *MenuUsecase) Create(ctx context.Context, menu *Menu) error {
	return uc.repo.Create(ctx, menu)
}

func (uc *MenuUsecase) Update(ctx context.Context, menu *Menu) error {
	return uc.repo.Update(ctx, menu)
}

func (uc *MenuUsecase) Delete(ctx context.Context, id uint) error {
	return uc.repo.Delete(ctx, id)
}

func (uc *MenuUsecase) Find(ctx context.Context, id uint) (*Menu, error) {
	return uc.repo.Find(ctx, id)
}

func (uc *MenuUsecase) SetAuthorityMenus(ctx context.Context, authorityID uint, ids []uint) error {
	return uc.repo.SetAuthorityMenus(ctx, authorityID, ids)
}

func (uc *MenuUsecase) AuthorityMenus(ctx context.Context, authorityID uint) ([]*Menu, error) {
	items, err := uc.repo.ListAuthorityMenus(ctx, authorityID)
	if err != nil {
		return nil, err
	}
	return items, nil
}

func (uc *MenuUsecase) RoleIDs(ctx context.Context, menuID uint) ([]uint, error) {
	return uc.repo.MenuRoleIDs(ctx, menuID)
}
func (uc *MenuUsecase) DefaultRouterRoleIDs(ctx context.Context, menuID uint) ([]uint, error) {
	return uc.repo.DefaultRouterRoleIDs(ctx, menuID)
}

func (uc *MenuUsecase) SetRoles(ctx context.Context, menuID uint, ids []uint) error {
	return uc.repo.SetMenuRoles(ctx, menuID, ids)
}
