// Package role 提供角色管理的业务逻辑服务
// 包含角色创建、查询、更新、删除、角色权限关联等功能
package role

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/internal/store"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"
	"gorm.io/gorm"
)

// Service 角色业务服务
type Service struct {
	roleStore           *postgres.RoleStore
	permissionStore     *postgres.PermissionStore
	rolePermissionStore *postgres.RolePermissionStore
	accountRoleStore    *postgres.AccountRoleStore
	shopRoleStore       *postgres.ShopRoleStore
}

// New 创建角色服务
func New(roleStore *postgres.RoleStore, permissionStore *postgres.PermissionStore, rolePermissionStore *postgres.RolePermissionStore, accountRoleStore *postgres.AccountRoleStore, shopRoleStore *postgres.ShopRoleStore) *Service {
	return &Service{
		roleStore:           roleStore,
		permissionStore:     permissionStore,
		rolePermissionStore: rolePermissionStore,
		accountRoleStore:    accountRoleStore,
		shopRoleStore:       shopRoleStore,
	}
}

// Create 创建角色
func (s *Service) Create(ctx context.Context, req *dto.CreateRoleRequest) (*dto.RoleResponse, error) {
	// 获取当前用户 ID
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	// 检查角色名是否已存在
	exists, err := s.roleStore.ExistsByName(ctx, req.RoleName, 0)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "检查角色名失败")
	}
	if exists {
		return nil, errors.New(errors.CodeRoleNameExists)
	}

	// 创建角色
	role := &model.Role{
		RoleName: req.RoleName,
		RoleDesc: req.RoleDesc,
		RoleType: req.RoleType,
		Status:   constants.StatusEnabled,
	}

	if err := s.roleStore.Create(ctx, role); err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "创建角色失败")
	}

	return toResponse(role), nil
}

// Get 获取角色
func (s *Service) Get(ctx context.Context, id uint) (*dto.RoleResponse, error) {
	role, err := s.roleStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeRoleNotFound, "角色不存在")
		}
		return nil, errors.Wrap(errors.CodeInternalError, err, "获取角色失败")
	}
	return toResponse(role), nil
}

// Update 更新角色
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateRoleRequest) (*dto.RoleResponse, error) {
	// 获取当前用户 ID
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	// 获取现有角色
	role, err := s.roleStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeRoleNotFound, "角色不存在")
		}
		return nil, errors.Wrap(errors.CodeInternalError, err, "获取角色失败")
	}

	// 如果修改了角色名，检查是否与其他角色重复
	if req.RoleName != nil && *req.RoleName != role.RoleName {
		exists, err := s.roleStore.ExistsByName(ctx, *req.RoleName, id)
		if err != nil {
			return nil, errors.Wrap(errors.CodeInternalError, err, "检查角色名失败")
		}
		if exists {
			return nil, errors.New(errors.CodeRoleNameExists)
		}
		role.RoleName = *req.RoleName
	}

	// 更新其他字段
	if req.RoleDesc != nil {
		role.RoleDesc = *req.RoleDesc
	}
	if req.Status != nil {
		role.Status = *req.Status
	}

	role.Updater = currentUserID

	if err := s.roleStore.Update(ctx, role); err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "更新角色失败")
	}

	return toResponse(role), nil
}

// Delete 软删除角色
func (s *Service) Delete(ctx context.Context, id uint) error {
	_, err := s.roleStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeRoleNotFound, "角色不存在")
		}
		return errors.Wrap(errors.CodeInternalError, err, "获取角色失败")
	}

	accountCount, err := s.accountRoleStore.CountByRoleID(ctx, id)
	if err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "检查角色分配情况失败")
	}

	shopCount, err := s.shopRoleStore.CountByRoleID(ctx, id)
	if err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "检查角色分配情况失败")
	}

	if accountCount > 0 || shopCount > 0 {
		return errors.New(errors.CodeRoleInUse, fmt.Sprintf("该角色已分配给 %d 个账号、%d 个店铺，请先移除相关分配后再删除", accountCount, shopCount))
	}

	if err := s.roleStore.Delete(ctx, id); err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "删除角色失败")
	}

	return nil
}

// List 查询角色列表
func (s *Service) List(ctx context.Context, req *dto.RoleListRequest) ([]*dto.RoleResponse, int64, error) {
	opts := &store.QueryOptions{
		Page:     req.Page,
		PageSize: req.PageSize,
		OrderBy:  "id DESC",
	}
	if opts.Page == 0 {
		opts.Page = 1
	}
	if opts.PageSize == 0 {
		opts.PageSize = constants.DefaultPageSize
	}

	filters := make(map[string]interface{})
	if req.RoleName != "" {
		filters["role_name"] = req.RoleName
	}
	if req.RoleType != nil {
		filters["role_type"] = *req.RoleType
	}
	if req.Status != nil {
		filters["status"] = *req.Status
	}

	roles, total, err := s.roleStore.List(ctx, opts, filters)
	if err != nil {
		return nil, 0, errors.Wrap(errors.CodeInternalError, err, "查询角色列表失败")
	}
	result := make([]*dto.RoleResponse, 0, len(roles))
	for _, role := range roles {
		result = append(result, toResponse(role))
	}
	return result, total, nil
}

// AssignPermissions 为角色分配权限
func (s *Service) AssignPermissions(ctx context.Context, roleID uint, permIDs []uint) ([]*model.RolePermission, error) {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return nil, errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	role, err := s.roleStore.GetByID(ctx, roleID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeRoleNotFound, "角色不存在")
		}
		return nil, errors.Wrap(errors.CodeInternalError, err, "获取角色失败")
	}

	permissions, err := s.permissionStore.GetByIDs(ctx, permIDs)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "获取权限失败")
	}

	if len(permissions) != len(permIDs) {
		return nil, errors.New(errors.CodePermissionNotFound, "部分权限不存在")
	}

	roleTypeStr := fmt.Sprintf("%d", role.RoleType)
	var invalidPermIDs []uint
	for _, perm := range permissions {
		if !contains(perm.AvailableForRoleTypes, roleTypeStr) {
			invalidPermIDs = append(invalidPermIDs, perm.ID)
		}
	}

	if len(invalidPermIDs) > 0 {
		return nil, errors.New(errors.CodeInvalidParam, fmt.Sprintf("权限 %v 不适用于此角色类型", invalidPermIDs))
	}

	// 批量获取已有权限集合，避免逐条 Exists 查询
	existingPermIDs, err := s.rolePermissionStore.GetPermIDsByRoleID(ctx, roleID)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "获取已有权限失败")
	}
	existingSet := make(map[uint]bool, len(existingPermIDs))
	for _, id := range existingPermIDs {
		existingSet[id] = true
	}

	var rps []*model.RolePermission
	for _, permID := range permIDs {
		if existingSet[permID] {
			continue
		}

		rp := &model.RolePermission{
			RoleID: roleID,
			PermID: permID,
			Status: constants.StatusEnabled,
		}
		if err := s.rolePermissionStore.Create(ctx, rp); err != nil {
			return nil, errors.Wrap(errors.CodeInternalError, err, "创建角色-权限关联失败")
		}
		rps = append(rps, rp)
	}

	return rps, nil
}

// GetPermissions 获取角色的所有权限
func (s *Service) GetPermissions(ctx context.Context, roleID uint) ([]*model.Permission, error) {
	// 检查角色存在
	_, err := s.roleStore.GetByID(ctx, roleID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeRoleNotFound, "角色不存在")
		}
		return nil, errors.Wrap(errors.CodeInternalError, err, "获取角色失败")
	}

	// 获取权限 ID 列表
	permIDs, err := s.rolePermissionStore.GetPermIDsByRoleID(ctx, roleID)
	if err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "获取角色权限 ID 失败")
	}

	if len(permIDs) == 0 {
		return []*model.Permission{}, nil
	}

	// 获取权限详情
	return s.permissionStore.GetByIDs(ctx, permIDs)
}

// RemovePermission 移除角色的权限
func (s *Service) RemovePermission(ctx context.Context, roleID, permID uint) error {
	_, err := s.roleStore.GetByID(ctx, roleID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeRoleNotFound, "角色不存在")
		}
		return errors.Wrap(errors.CodeInternalError, err, "获取角色失败")
	}

	if err := s.rolePermissionStore.Delete(ctx, roleID, permID); err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "删除角色-权限关联失败")
	}

	return nil
}

// BatchRemovePermissions 批量移除角色的权限
func (s *Service) BatchRemovePermissions(ctx context.Context, roleID uint, permIDs []uint) error {
	_, err := s.roleStore.GetByID(ctx, roleID)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeRoleNotFound, "角色不存在")
		}
		return errors.Wrap(errors.CodeInternalError, err, "获取角色失败")
	}

	if err := s.rolePermissionStore.BatchDelete(ctx, roleID, permIDs); err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "批量删除角色-权限关联失败")
	}

	return nil
}

// UpdateStatus 更新角色状态
// 禁用角色时，若角色已分配给账号或店铺，则拒绝操作
func (s *Service) UpdateStatus(ctx context.Context, id uint, status int) error {
	currentUserID := middleware.GetUserIDFromContext(ctx)
	if currentUserID == 0 {
		return errors.New(errors.CodeUnauthorized, "未授权访问")
	}

	role, err := s.roleStore.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeRoleNotFound, "角色不存在")
		}
		return errors.Wrap(errors.CodeInternalError, err, "获取角色失败")
	}

	// 禁用角色时检查是否已有分配
	if status == constants.StatusDisabled {
		accountCount, err := s.accountRoleStore.CountByRoleID(ctx, id)
		if err != nil {
			return errors.Wrap(errors.CodeInternalError, err, "检查角色分配情况失败")
		}

		shopCount, err := s.shopRoleStore.CountByRoleID(ctx, id)
		if err != nil {
			return errors.Wrap(errors.CodeInternalError, err, "检查角色分配情况失败")
		}

		if accountCount > 0 || shopCount > 0 {
			return errors.New(errors.CodeRoleInUse, fmt.Sprintf("该角色已分配给 %d 个账号、%d 个店铺，请先移除相关分配后再禁用", accountCount, shopCount))
		}
	}

	role.Status = status
	role.Updater = currentUserID

	if err := s.roleStore.Update(ctx, role); err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "更新角色状态失败")
	}

	return nil
}

// toResponse 将 model.Role 转换为 dto.RoleResponse
func toResponse(role *model.Role) *dto.RoleResponse {
	return &dto.RoleResponse{
		ID:                   role.ID,
		RoleName:             role.RoleName,
		RoleDesc:             role.RoleDesc,
		RoleType:             role.RoleType,
		Status:               role.Status,
		DefaultCreditEnabled: role.DefaultCreditEnabled,
		DefaultCreditLimit:   role.DefaultCreditLimit,
		DefaultCreditScope:   "new_shops_only",
		Creator:              role.Creator,
		Updater:              role.Updater,
		CreatedAt:            role.CreatedAt.Format(time.RFC3339),
		UpdatedAt:            role.UpdatedAt.Format(time.RFC3339),
	}
}

func contains(availableForRoleTypes, roleTypeStr string) bool {
	types := strings.Split(availableForRoleTypes, ",")
	for _, t := range types {
		if strings.TrimSpace(t) == roleTypeStr {
			return true
		}
	}
	return false
}
