package system

import (
	"context"
	"errors"
	"strconv"

	"kra/internal/conf"
)

var (
	ErrAuthorityExists      = errors.New("存在相同角色id")
	ErrAuthorityNotFound    = errors.New("该角色不存在")
	ErrAuthorityHasUsers    = errors.New("此角色有用户正在使用禁止删除")
	ErrAuthorityHasChildren = errors.New("此角色存在子角色不允许删除")
	ErrAuthorityIDInvalid   = errors.New("您提交的角色ID不合法")
)

// AuthorityFull 完整角色实体（包含关联）
type AuthorityFull struct {
	Authority
	DataAuthorityId []*Authority
	Children        []*AuthorityFull
	SysBaseMenus    []*BaseMenu
}

// CasbinInfo Casbin规则信息
type CasbinInfo struct {
	Path   string
	Method string
}

// DefaultCasbin 默认Casbin规则
func DefaultCasbin() []CasbinInfo {
	return []CasbinInfo{
		{Path: "/menu/getMenu", Method: "POST"},
		{Path: "/jwt/jsonInBlacklist", Method: "POST"},
		{Path: "/base/login", Method: "POST"},
		{Path: "/user/changePassword", Method: "POST"},
		{Path: "/user/setUserAuthority", Method: "POST"},
		{Path: "/user/getUserInfo", Method: "GET"},
		{Path: "/user/setSelfInfo", Method: "PUT"},
		{Path: "/fileUploadAndDownload/upload", Method: "POST"},
		{Path: "/sysDictionary/findSysDictionary", Method: "GET"},
	}
}

// DefaultMenu 默认菜单
func DefaultMenu() []*BaseMenu {
	return []*BaseMenu{{
		ID:        1,
		ParentId:  0,
		Path:      "dashboard",
		Name:      "dashboard",
		Component: "view/dashboard/index.vue",
		Sort:      1,
		Title:     "仪表盘",
		Icon:      "setting",
	}}
}

// AuthorityRepo 角色仓储接口
type AuthorityRepo interface {
	Create(ctx context.Context, auth *Authority) error
	Update(ctx context.Context, auth *Authority) error
	Delete(ctx context.Context, authorityId uint) error
	FindByID(ctx context.Context, authorityId uint) (*Authority, error)
	FindByIDWithDataAuthority(ctx context.Context, authorityId uint) (*AuthorityFull, error)
	FindByParentID(ctx context.Context, parentId uint) ([]*AuthorityFull, error)
	FindChildren(ctx context.Context, authorityId uint) ([]*AuthorityFull, error)
	HasUsers(ctx context.Context, authorityId uint) (bool, error)
	HasChildren(ctx context.Context, authorityId uint) (bool, error)
	GetParentAuthorityID(ctx context.Context, authorityId uint) (uint, error)
	SetDataAuthority(ctx context.Context, authorityId uint, dataAuthorityIds []uint) error
	SetMenuAuthority(ctx context.Context, authorityId uint, menuIds []uint) error
	GetMenuIds(ctx context.Context, authorityId uint) ([]uint, error)
	CopyAuthorityBtns(ctx context.Context, oldAuthorityId, newAuthorityId uint) error
	DeleteAuthorityRelations(ctx context.Context, authorityId uint) error
}

// AuthorityUsecase 角色用例
type AuthorityUsecase struct {
	repo          AuthorityRepo
	casbinRepo    CasbinRepo
	useStrictAuth bool
}

// NewAuthorityUsecase 创建角色用例
func NewAuthorityUsecase(repo AuthorityRepo, casbinRepo CasbinRepo, useStrictAuth conf.UseStrictAuth) *AuthorityUsecase {
	return &AuthorityUsecase{
		repo:          repo,
		casbinRepo:    casbinRepo,
		useStrictAuth: bool(useStrictAuth),
	}
}

// CreateAuthority 创建角色
func (uc *AuthorityUsecase) CreateAuthority(ctx context.Context, auth *Authority) (*Authority, error) {
	// 检查是否存在相同角色ID
	existing, _ := uc.repo.FindByID(ctx, auth.AuthorityId)
	if existing != nil {
		return nil, ErrAuthorityExists
	}

	// 创建角色
	if err := uc.repo.Create(ctx, auth); err != nil {
		return nil, err
	}

	// 设置默认菜单
	defaultMenus := DefaultMenu()
	menuIds := make([]uint, len(defaultMenus))
	for i, m := range defaultMenus {
		menuIds[i] = m.ID
	}
	if err := uc.repo.SetMenuAuthority(ctx, auth.AuthorityId, menuIds); err != nil {
		return nil, err
	}

	// 添加默认Casbin规则
	casbinInfos := DefaultCasbin()
	rules := make([][]string, len(casbinInfos))
	for i, v := range casbinInfos {
		rules[i] = []string{uintToStr(auth.AuthorityId), v.Path, v.Method}
	}
	if err := uc.casbinRepo.AddPolicies(rules); err != nil {
		return nil, err
	}

	return auth, nil
}

// CopyAuthority 复制角色
func (uc *AuthorityUsecase) CopyAuthority(ctx context.Context, adminAuthorityID, oldAuthorityId uint, newAuth *Authority) (*Authority, error) {
	// 检查新角色ID是否已存在
	existing, _ := uc.repo.FindByID(ctx, newAuth.AuthorityId)
	if existing != nil {
		return nil, ErrAuthorityExists
	}

	// 获取旧角色的菜单
	menuIds, err := uc.repo.GetMenuIds(ctx, oldAuthorityId)
	if err != nil {
		return nil, err
	}

	// 创建新角色
	if err := uc.repo.Create(ctx, newAuth); err != nil {
		return nil, err
	}

	// 复制菜单权限
	if err := uc.repo.SetMenuAuthority(ctx, newAuth.AuthorityId, menuIds); err != nil {
		return nil, err
	}

	// 复制按钮权限
	if err := uc.repo.CopyAuthorityBtns(ctx, oldAuthorityId, newAuth.AuthorityId); err != nil {
		return nil, err
	}

	// 复制Casbin规则
	paths := uc.casbinRepo.GetPolicyPathByAuthorityId(oldAuthorityId)
	if err := uc.casbinRepo.UpdateCasbin(adminAuthorityID, newAuth.AuthorityId, paths); err != nil {
		_ = uc.DeleteAuthority(ctx, newAuth.AuthorityId)
		return nil, err
	}

	return newAuth, nil
}

// UpdateAuthority 更新角色
func (uc *AuthorityUsecase) UpdateAuthority(ctx context.Context, auth *Authority) (*Authority, error) {
	existing, err := uc.repo.FindByID(ctx, auth.AuthorityId)
	if err != nil {
		return nil, errors.New("查询角色数据失败")
	}
	if existing == nil {
		return nil, ErrAuthorityNotFound
	}

	if err := uc.repo.Update(ctx, auth); err != nil {
		return nil, err
	}
	return auth, nil
}

// DeleteAuthority 删除角色
func (uc *AuthorityUsecase) DeleteAuthority(ctx context.Context, authorityId uint) error {
	// 检查角色是否存在
	existing, err := uc.repo.FindByID(ctx, authorityId)
	if err != nil || existing == nil {
		return ErrAuthorityNotFound
	}

	// 检查是否有用户使用此角色
	hasUsers, err := uc.repo.HasUsers(ctx, authorityId)
	if err != nil {
		return err
	}
	if hasUsers {
		return ErrAuthorityHasUsers
	}

	// 检查是否有子角色
	hasChildren, err := uc.repo.HasChildren(ctx, authorityId)
	if err != nil {
		return err
	}
	if hasChildren {
		return ErrAuthorityHasChildren
	}

	// 删除关联关系
	if err := uc.repo.DeleteAuthorityRelations(ctx, authorityId); err != nil {
		return err
	}

	// 删除角色
	if err := uc.repo.Delete(ctx, authorityId); err != nil {
		return err
	}

	// 删除Casbin规则
	uc.casbinRepo.RemoveFilteredPolicy(uintToStr(authorityId))

	return nil
}

// GetAuthorityInfoList 获取角色列表（树形）
func (uc *AuthorityUsecase) GetAuthorityInfoList(ctx context.Context, authorityID uint) ([]*AuthorityFull, error) {
	auth, err := uc.repo.FindByID(ctx, authorityID)
	if err != nil {
		return nil, err
	}

	var authorities []*AuthorityFull
	if uc.useStrictAuth {
		if auth.ParentId == nil || *auth.ParentId == 0 {
			// 顶级角色可以修改自己的权限和以下权限
			authFull, err := uc.repo.FindByIDWithDataAuthority(ctx, authorityID)
			if err != nil {
				return nil, err
			}
			authorities = []*AuthorityFull{authFull}
		} else {
			// 非顶级角色只能修改以下权限
			authorities, err = uc.repo.FindByParentID(ctx, authorityID)
			if err != nil {
				return nil, err
			}
		}
	} else {
		authorities, err = uc.repo.FindByParentID(ctx, 0)
		if err != nil {
			return nil, err
		}
	}

	// 递归获取子角色
	for i := range authorities {
		if err := uc.findChildrenAuthority(ctx, authorities[i]); err != nil {
			return nil, err
		}
	}

	return authorities, nil
}

func (uc *AuthorityUsecase) findChildrenAuthority(ctx context.Context, authority *AuthorityFull) error {
	children, err := uc.repo.FindChildren(ctx, authority.AuthorityId)
	if err != nil {
		return err
	}
	authority.Children = children
	for i := range authority.Children {
		if err := uc.findChildrenAuthority(ctx, authority.Children[i]); err != nil {
			return err
		}
	}
	return nil
}

// GetStructAuthorityList 获取角色ID列表（包含子角色）
func (uc *AuthorityUsecase) GetStructAuthorityList(ctx context.Context, authorityID uint) ([]uint, error) {
	auth, err := uc.repo.FindByID(ctx, authorityID)
	if err != nil {
		return nil, err
	}

	var list []uint
	children, err := uc.repo.FindChildren(ctx, authorityID)
	if err != nil {
		return nil, err
	}

	for _, child := range children {
		list = append(list, child.AuthorityId)
		childList, err := uc.GetStructAuthorityList(ctx, child.AuthorityId)
		if err == nil {
			list = append(list, childList...)
		}
	}

	if auth.ParentId == nil || *auth.ParentId == 0 {
		list = append(list, authorityID)
	}

	return list, nil
}

// CheckAuthorityIDAuth 检查角色ID权限
func (uc *AuthorityUsecase) CheckAuthorityIDAuth(ctx context.Context, authorityID, targetID uint) error {
	if !uc.useStrictAuth {
		return nil
	}

	authIDs, err := uc.GetStructAuthorityList(ctx, authorityID)
	if err != nil {
		return err
	}

	for _, id := range authIDs {
		if id == targetID {
			return nil
		}
	}

	return ErrAuthorityIDInvalid
}

// GetAuthorityInfo 获取角色信息
func (uc *AuthorityUsecase) GetAuthorityInfo(ctx context.Context, authorityId uint) (*AuthorityFull, error) {
	return uc.repo.FindByIDWithDataAuthority(ctx, authorityId)
}

// SetDataAuthority 设置角色资源权限
func (uc *AuthorityUsecase) SetDataAuthority(ctx context.Context, adminAuthorityID uint, authorityId uint, dataAuthorityIds []uint) error {
	// 检查权限
	checkIDs := append([]uint{authorityId}, dataAuthorityIds...)
	for _, id := range checkIDs {
		if err := uc.CheckAuthorityIDAuth(ctx, adminAuthorityID, id); err != nil {
			return err
		}
	}

	return uc.repo.SetDataAuthority(ctx, authorityId, dataAuthorityIds)
}

// SetMenuAuthority 设置角色菜单权限
func (uc *AuthorityUsecase) SetMenuAuthority(ctx context.Context, authorityId uint, menuIds []uint) error {
	return uc.repo.SetMenuAuthority(ctx, authorityId, menuIds)
}

// GetParentAuthorityID 获取父角色ID
func (uc *AuthorityUsecase) GetParentAuthorityID(ctx context.Context, authorityId uint) (uint, error) {
	return uc.repo.GetParentAuthorityID(ctx, authorityId)
}

// 辅助函数
func uintToStr(n uint) string {
	return strconv.FormatUint(uint64(n), 10)
}
