package system

import (
	"context"
	"time"
)

type AuthorityAccessRepo interface {
	CreateAuthority(context.Context, *Authority) error
	CopyAuthority(context.Context, uint, *Authority) error
	UpdateAuthority(context.Context, *Authority) error
	DeleteAuthority(context.Context, uint) error
	ListAuthorities(context.Context) ([]*Authority, error)
	SetAuthorityUsers(context.Context, uint, []uint) error
	AuthorityUserIDs(context.Context, uint) ([]uint, error)
	SetDataScope(context.Context, uint, int, []uint) error
	DataScopeDepartmentIDs(context.Context, uint) ([]uint, error)
	ResolveDataScope(context.Context, uint, uint) (DataScope, error)
}

// Authority is the domain representation of an administration role. Keep the
// audit timestamps because exports and API clients use them even though the
// administration screens do not display them.
type Authority struct {
	CreatedAt     time.Time
	UpdatedAt     time.Time
	DeletedAt     *time.Time
	AuthorityID   uint
	AuthorityName string
	ParentID      *uint
	DataScope     int
	DefaultRouter string
	Children      []*Authority
	Menus         []*Menu
}

type AuthorityUsecase struct{ AuthorityAccessRepo }

func NewAuthorityUsecase(repo AuthorityAccessRepo) *AuthorityUsecase {
	return &AuthorityUsecase{AuthorityAccessRepo: repo}
}

func (uc *AuthorityUsecase) AuthorityTree(ctx context.Context) ([]*Authority, error) {
	items, err := uc.ListAuthorities(ctx)
	if err != nil {
		return nil, err
	}
	byID := make(map[uint]*Authority, len(items))
	for _, item := range items {
		item.Children = nil
		byID[item.AuthorityID] = item
	}
	roots := make([]*Authority, 0)
	for _, item := range items {
		if item.ParentID != nil && *item.ParentID != 0 && byID[*item.ParentID] != nil {
			parent := byID[*item.ParentID]
			parent.Children = append(parent.Children, item)
		} else if item.ParentID != nil && *item.ParentID == 0 {
			roots = append(roots, item)
		}
	}
	// In strict-authority mode a non-root actor receives its direct
	// children as the top-level result even though their parent is omitted.
	if len(roots) == 0 {
		if actor, ok := ActorFromContext(ctx); ok {
			for _, item := range items {
				if item.ParentID != nil && *item.ParentID == actor.AuthorityID {
					roots = append(roots, item)
				}
			}
		}
	}
	return roots, nil
}
