package service

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

	"github.com/user-management-system/internal/auth"
	"github.com/user-management-system/internal/domain"
	"github.com/user-management-system/internal/pagination"
	"github.com/user-management-system/internal/repository"
	"gorm.io/gorm"
)

// UserService 用户服务
type UserService struct {
	userRepo              *repository.UserRepository
	userRoleRepo          *repository.UserRoleRepository
	roleRepo              *repository.RoleRepository
	passwordHistoryRepo   *repository.PasswordHistoryRepository
}

const passwordHistoryLimit = 5 // 保留最近5条密码历史

// NewUserService 创建用户服务实例
func NewUserService(
	userRepo *repository.UserRepository,
	userRoleRepo *repository.UserRoleRepository,
	roleRepo *repository.RoleRepository,
	passwordHistoryRepo *repository.PasswordHistoryRepository,
) *UserService {
	return &UserService{
		userRepo:            userRepo,
		userRoleRepo:        userRoleRepo,
		roleRepo:            roleRepo,
		passwordHistoryRepo: passwordHistoryRepo,
	}
}

// ChangePassword 修改用户密码（含历史记录检查）
func (s *UserService) ChangePassword(ctx context.Context, userID int64, oldPassword, newPassword string) error {
	if s.userRepo == nil {
		return errors.New("user repository is not configured")
	}

	user, err := s.userRepo.GetByID(ctx, userID)
	if err != nil {
		return errors.New("用户不存在")
	}

	// 验证旧密码
	if strings.TrimSpace(oldPassword) == "" {
		return errors.New("请输入当前密码")
	}
	if !auth.VerifyPassword(user.Password, oldPassword) {
		return errors.New("当前密码不正确")
	}

	// 检查新密码强度
	if strings.TrimSpace(newPassword) == "" {
		return errors.New("新密码不能为空")
	}
	if err := validatePasswordStrength(newPassword, 8, false); err != nil {
		return err
	}

	// 检查密码历史（需要明文密码比对，必须在哈希之前）
	if s.passwordHistoryRepo != nil {
		histories, err := s.passwordHistoryRepo.GetByUserID(ctx, userID, passwordHistoryLimit)
		if err == nil && len(histories) > 0 {
			for _, h := range histories {
				if auth.VerifyPassword(h.PasswordHash, newPassword) {
					return errors.New("新密码不能与最近5次密码相同")
				}
			}
		}
	}

	// 计算一次哈希，用于更新密码和保存历史（避免 Argon2id 重复计算的高成本）
	newHashedPassword, hashErr := auth.HashPassword(newPassword)
	if hashErr != nil {
		return errors.New("密码哈希失败")
	}

	// 保存新密码到历史记录（异步，不阻塞密码更新）
	if s.passwordHistoryRepo != nil {
		// #nosec G118 - 使用带超时的独立 context（不能使用请求 ctx，该 goroutine 在请求完成后仍可能运行）
		go func(hashedPw string) { // #nosec G118
			bgCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
			defer cancel()
			_ = s.passwordHistoryRepo.Create(bgCtx, &domain.PasswordHistory{
				UserID:       userID,
				PasswordHash: hashedPw,
			})
			_ = s.passwordHistoryRepo.DeleteOldRecords(bgCtx, userID, passwordHistoryLimit)
		}(newHashedPassword)
	}

	// 更新密码（使用同一哈希值）
	user.Password = newHashedPassword
	return s.userRepo.Update(ctx, user)
}

// GetByID 根据ID获取用户
func (s *UserService) GetByID(ctx context.Context, id int64) (*domain.User, error) {
	return s.userRepo.GetByID(ctx, id)
}

// GetByEmail 根据邮箱获取用户
func (s *UserService) GetByEmail(ctx context.Context, email string) (*domain.User, error) {
	return s.userRepo.GetByEmail(ctx, email)
}

// Create 创建用户
func (s *UserService) Create(ctx context.Context, user *domain.User) error {
	return s.userRepo.Create(ctx, user)
}

// Update 更新用户
func (s *UserService) Update(ctx context.Context, user *domain.User) error {
	return s.userRepo.Update(ctx, user)
}

// Delete 删除用户
func (s *UserService) Delete(ctx context.Context, id int64) error {
	return s.userRepo.Delete(ctx, id)
}

// List 获取用户列表
func (s *UserService) List(ctx context.Context, offset, limit int) ([]*domain.User, int64, error) {
	return s.userRepo.List(ctx, offset, limit)
}

// ListCursorRequest 用户游标分页请求
type ListCursorRequest struct {
	Keyword     string `form:"keyword"`
	Status      int    `form:"status"` // -1=全部
	RoleIDs     []int64
	CreatedFrom *time.Time
	CreatedTo   *time.Time
	SortBy      string // created_at, last_login_time, username
	SortOrder   string // asc, desc
	Cursor      string `form:"cursor"`
	Size        int    `form:"size"`
}

// ListCursor 游标分页获取用户列表（推荐使用）
func (s *UserService) ListCursor(ctx context.Context, req *ListCursorRequest) (*CursorResult, error) {
	size := pagination.ClampPageSize(req.Size)

	cursor, err := pagination.Decode(req.Cursor)
	if err != nil {
		return nil, fmt.Errorf("invalid cursor: %w", err)
	}

	filter := &repository.AdvancedFilter{
		Keyword:       req.Keyword,
		Status:        req.Status,
		RoleIDs:       req.RoleIDs,
		CreatedFrom:   req.CreatedFrom,
		CreatedTo:     req.CreatedTo,
		SortBy:        req.SortBy,
		SortOrder:     req.SortOrder,
	}

	users, hasMore, err := s.userRepo.ListCursor(ctx, filter, size, cursor)
	if err != nil {
		return nil, err
	}

	nextCursor := ""
	if len(users) > 0 {
		last := users[len(users)-1]
		nextCursor = pagination.BuildNextCursor(last.ID, last.CreatedAt)
	}

	return &CursorResult{
		Items:      users,
		NextCursor: nextCursor,
		HasMore:    hasMore,
		PageSize:   size,
	}, nil
}

// UpdateStatus 更新用户状态
func (s *UserService) UpdateStatus(ctx context.Context, id int64, status domain.UserStatus) error {
	return s.userRepo.UpdateStatus(ctx, id, status)
}

// BatchUpdateStatusRequest 批量更新状态请求
type BatchUpdateStatusRequest struct {
	IDs    []int64            `json:"ids" binding:"required,min=1"`
	Status domain.UserStatus   `json:"status" binding:"required"`
}

// BatchDeleteRequest 批量删除请求
type BatchDeleteRequest struct {
	IDs []int64 `json:"ids" binding:"required,min=1"`
}

// BatchUpdateStatus 批量更新用户状态
func (s *UserService) BatchUpdateStatus(ctx context.Context, req *BatchUpdateStatusRequest) (int64, error) {
	err := s.userRepo.BatchUpdateStatus(ctx, req.IDs, req.Status)
	return int64(len(req.IDs)), err
}

// BatchDelete 批量删除用户
func (s *UserService) BatchDelete(ctx context.Context, req *BatchDeleteRequest) (int64, error) {
	err := s.userRepo.BatchDelete(ctx, req.IDs)
	return int64(len(req.IDs)), err
}

// GetUserRoles 获取用户的所有角色
func (s *UserService) GetUserRoles(ctx context.Context, userID int64) ([]*domain.Role, error) {
	// 检查用户是否存在
	if _, err := s.userRepo.GetByID(ctx, userID); err != nil {
		return nil, err
	}

	// 获取用户角色关联
	userRoles, err := s.userRoleRepo.GetByUserID(ctx, userID)
	if err != nil {
		return nil, err
	}

	if len(userRoles) == 0 {
		return []*domain.Role{}, nil
	}

	// 获取角色ID列表
	roleIDs := make([]int64, len(userRoles))
	for i, ur := range userRoles {
		roleIDs[i] = ur.RoleID
	}

	// 批量获取角色详情（消除 N+1 查询）
	roles, err := s.roleRepo.GetByIDs(ctx, roleIDs)
	if err != nil {
		return nil, fmt.Errorf("failed to fetch roles: %w", err)
	}

	return roles, nil
}

// AssignRoles 分配用户角色
func (s *UserService) AssignRoles(ctx context.Context, userID int64, roleIDs []int64) error {
	// 检查用户是否存在
	if _, err := s.userRepo.GetByID(ctx, userID); err != nil {
		return err
	}

	// 验证所有角色存在（预先验证，避免在事务内做不必要的查询）
	for _, roleID := range roleIDs {
		if _, err := s.roleRepo.GetByID(ctx, roleID); err != nil {
			return fmt.Errorf("角色 %d 不存在", roleID)
		}
	}

	// 构建新的用户角色关联
	var userRoles []*domain.UserRole
	for _, roleID := range roleIDs {
		userRoles = append(userRoles, &domain.UserRole{
			UserID: userID,
			RoleID: roleID,
		})
	}

	// 使用事务包装删旧建新操作，确保原子性
	return s.userRoleRepo.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		if err := s.userRoleRepo.WithTx(tx).DeleteByUserID(ctx, userID); err != nil {
			return err
		}
		return s.userRoleRepo.WithTx(tx).BatchCreate(ctx, userRoles)
	})
}

// getAdminRoleID looks up the admin role ID by code to avoid hardcoded magic numbers.
func (s *UserService) getAdminRoleID(ctx context.Context) (int64, error) {
	adminRole, err := s.roleRepo.GetByCode(ctx, "admin")
	if err != nil {
		return 0, fmt.Errorf("failed to find admin role: %w", err)
	}
	return adminRole.ID, nil
}

// ListAdmins 获取所有管理员
func (s *UserService) ListAdmins(ctx context.Context) ([]*domain.User, error) {
	// 获取管理员角色ID列表
	adminRoleID, err := s.getAdminRoleID(ctx)
	if err != nil {
		return nil, err
	}
	adminUserIDs, err := s.userRoleRepo.GetUserIDByRoleID(ctx, adminRoleID)
	if err != nil {
		return nil, err
	}

	if len(adminUserIDs) == 0 {
		return []*domain.User{}, nil
	}

	// 批量获取所有管理员用户（消除 N+1 查询）
	admins, err := s.userRepo.GetByIDs(ctx, adminUserIDs)
	if err != nil {
		return nil, fmt.Errorf("failed to fetch admin users: %w", err)
	}

	return admins, nil
}

// CreateAdmin 创建管理员（事务性）
func (s *UserService) CreateAdmin(ctx context.Context, req *CreateAdminRequest) (*domain.User, error) {
	// 检查用户名是否已存在
	existingUser, err := s.userRepo.GetByUsername(ctx, req.Username)
	if err == nil && existingUser != nil {
		return nil, errors.New("用户名已存在")
	}

	// 预先查询管理员角色 ID（避免在事务中使用 roleRepo）
	adminRoleID, err := s.getAdminRoleID(ctx)
	if err != nil {
		return nil, err
	}

	// 创建用户
	hashedPassword, err := auth.HashPassword(req.Password)
	if err != nil {
		return nil, errors.New("密码哈希失败")
	}

	user := &domain.User{
		Username: req.Username,
		Password: hashedPassword,
		Status:   domain.UserStatusActive,
	}

	if req.Email != "" {
		user.Email = &req.Email
	}
	if req.Nickname != "" {
		user.Nickname = req.Nickname
	}

	// 使用事务创建用户和分配角色
	err = s.userRepo.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		if err := tx.Create(user).Error; err != nil {
			return err
		}

		// 分配管理员角色
		userRole := &domain.UserRole{
			UserID: user.ID,
			RoleID: adminRoleID,
		}
		if err := tx.Create(userRole).Error; err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	return user, nil
}

// DeleteAdmin 删除管理员（移除管理员角色）
func (s *UserService) DeleteAdmin(ctx context.Context, userID int64, currentUserID int64) error {
	// 检查用户是否存在
	if _, err := s.userRepo.GetByID(ctx, userID); err != nil {
		return err
	}

	// 不能删除自己
	if currentUserID == userID {
		return errors.New("不能删除自己")
	}

	// 检查是否是最后一个管理员（保护）
	adminRoleID, err := s.getAdminRoleID(ctx)
	if err != nil {
		return err
	}
	adminUserRoles, err := s.userRoleRepo.GetByRoleID(ctx, adminRoleID)
	if err != nil {
		return err
	}
	if len(adminUserRoles) <= 1 {
		return errors.New("不能删除最后一个管理员")
	}

	// 删除用户的管理员角色
	return s.userRoleRepo.DeleteByUserAndRole(ctx, userID, adminRoleID)
}

// CreateAdminRequest 创建管理员请求
type CreateAdminRequest struct {
	Username string `json:"username" binding:"required"`
	Password string `json:"password" binding:"required"`
	Email   string `json:"email"`
	Nickname string `json:"nickname"`
}
