package service

import (
	"encoding/json"
	"fmt"
	"regexp"
	"time"

	"be.ems/src/framework/constants"
	"be.ems/src/framework/i18n"
	"be.ems/src/framework/utils/crypto"
	"be.ems/src/framework/utils/parse"
	"be.ems/src/modules/system/model"
	"be.ems/src/modules/system/repository"
)

// NewSysUser 实例化服务层
var NewSysUser = &SysUser{
	sysUserRepository:         repository.NewSysUser,
	sysRoleRepository:         repository.NewSysRole,
	sysDeptRepository:         repository.NewSysDept,
	sysUserRoleRepository:     repository.NewSysUserRole,
	sysUserPostRepository:     repository.NewSysUserPost,
	sysDictTypeService:        NewSysDictType,
	sysDictDataService:        NewSysDictData,
	sysConfigService:          NewSysConfig,
	sysLogUserPasswordService: NewSysLogUserPassword,
}

// SysUser 用户 服务层处理
type SysUser struct {
	sysUserRepository         *repository.SysUser     // 用户服务
	sysRoleRepository         *repository.SysRole     // 角色服务
	sysDeptRepository         *repository.SysDept     // 部门服务
	sysUserRoleRepository     *repository.SysUserRole // 用户与角色服务
	sysUserPostRepository     *repository.SysUserPost // 用户与岗位服务
	sysDictTypeService        *SysDictType            // 字典类型服务
	sysDictDataService        *SysDictData            // 字典数据服务
	sysConfigService          *SysConfig              // 参数配置服务
	sysLogUserPasswordService *SysLogUserPassword     // 用户密码变更日志服务
}

// FindByPage 分页查询列表数据
func (s SysUser) FindByPage(query map[string]string, dataScopeSQL string) ([]model.SysUser, int64) {
	rows, total := s.sysUserRepository.SelectByPage(query, dataScopeSQL)
	for i, v := range rows {
		// 部门
		deptInfo := s.sysDeptRepository.SelectById(v.DeptId)
		rows[i].Dept = &deptInfo
		// 角色
		roleArr := s.sysRoleRepository.SelectByUserId(v.UserId)
		roleIds := make([]int64, 0)
		roles := make([]*model.SysRole, 0)
		for _, role := range roleArr {
			roles = append(roles, &role)
			roleIds = append(roleIds, role.RoleId)
		}
		rows[i].Roles = roles
		rows[i].RoleIds = roleIds
	}
	return rows, total
}

// Find 查询数据
func (s SysUser) Find(sysUser model.SysUser) []model.SysUser {
	return s.sysUserRepository.Select(sysUser)
}

// FindById 通过ID查询信息
func (s SysUser) FindById(userId int64) model.SysUser {
	userInfo := model.SysUser{}
	if userId <= 0 {
		return userInfo
	}
	users := s.sysUserRepository.SelectByIds([]int64{userId})
	if len(users) > 0 {
		userInfo = users[0]
		// 部门
		deptInfo := s.sysDeptRepository.SelectById(userInfo.DeptId)
		userInfo.Dept = &deptInfo
		// 角色
		roleArr := s.sysRoleRepository.SelectByUserId(userInfo.UserId)
		roleIds := make([]int64, 0)
		roles := make([]*model.SysRole, 0)
		for _, role := range roleArr {
			roles = append(roles, &role)
			roleIds = append(roleIds, role.RoleId)
		}
		userInfo.Roles = roles
		userInfo.RoleIds = roleIds
	}
	return userInfo
}

// Insert 新增信息
func (s SysUser) Insert(sysUser model.SysUser) int64 {
	// 新增用户信息
	insertId := s.sysUserRepository.Insert(sysUser)
	if insertId > 0 {
		s.insertUserRole(insertId, sysUser.RoleIds) // 新增用户角色信息
		s.insertUserPost(insertId, sysUser.PostIds) // 新增用户岗位信息
	}
	return insertId
}

// insertUserRole 新增用户角色信息
func (s SysUser) insertUserRole(userId int64, roleIds []int64) int64 {
	if userId <= 0 || len(roleIds) <= 0 {
		return 0
	}

	var arr []model.SysUserRole
	for _, roleId := range roleIds {
		// 系统管理员角色禁止操作，只能通过配置指定用户ID分配
		if roleId <= 0 || roleId == constants.SYS_ROLE_SYSTEM_ID {
			continue
		}
		arr = append(arr, model.SysUserRole{
			UserId: userId, RoleId: roleId,
		})
	}

	return s.sysUserRoleRepository.BatchInsert(arr)
}

// insertUserPost 新增用户岗位信息
func (s SysUser) insertUserPost(userId int64, postIds []int64) int64 {
	if userId <= 0 || len(postIds) <= 0 {
		return 0
	}

	var arr []model.SysUserPost
	for _, postId := range postIds {
		if postId <= 0 {
			continue
		}
		arr = append(arr, model.SysUserPost{
			UserId: userId, PostId: postId,
		})
	}

	return s.sysUserPostRepository.BatchInsert(arr)
}

// Update 修改信息
func (s SysUser) Update(sysUser model.SysUser) int64 {
	rows := s.sysUserRepository.Update(sysUser)
	// 记录密码变更日志
	if rows > 0 && sysUser.Password != "" {
		s.sysLogUserPasswordService.Insert(model.SysLogUserPassword{
			UserId:   sysUser.UserId,
			UserName: sysUser.UserName,
			Password: crypto.BcryptHash(sysUser.Password),
			CreateBy: sysUser.UpdateBy,
		})
	}
	return rows
}

// UpdateUserAndRolePost 修改用户信息同时更新角色和岗位
func (s SysUser) UpdateUserAndRolePost(sysUser model.SysUser) int64 {
	// 删除用户与角色关联
	s.sysUserRoleRepository.DeleteByUserIds([]int64{sysUser.UserId})
	// 新增用户角色信息
	s.insertUserRole(sysUser.UserId, sysUser.RoleIds)
	// 删除用户与岗位关联
	s.sysUserPostRepository.DeleteByUserIds([]int64{sysUser.UserId})
	// 新增用户岗位信息
	s.insertUserPost(sysUser.UserId, sysUser.PostIds)
	return s.sysUserRepository.Update(sysUser)
}

// DeleteByIds 批量删除信息
func (s SysUser) DeleteByIds(userIds []int64) (int64, error) {
	// 检查是否存在
	users := s.sysUserRepository.SelectByIds(userIds)
	if len(users) <= 0 {
		return 0, fmt.Errorf("没有权限访问用户数据！")
	}
	if len(users) == len(userIds) {
		s.sysUserRoleRepository.DeleteByUserIds(userIds)    // 删除用户与角色关联
		s.sysUserPostRepository.DeleteByUserIds(userIds)    // 删除用户与岗位关联
		s.sysLogUserPasswordService.DeleteByUserId(userIds) // 删除用户密码变更日志
		return s.sysUserRepository.DeleteByIds(userIds), nil
	}
	return 0, fmt.Errorf("删除用户信息失败！")
}

// CheckUniqueByUserName 检查用户名称是否唯一
func (s SysUser) CheckUniqueByUserName(userName string, userId int64) bool {
	uniqueId := s.sysUserRepository.CheckUnique(model.SysUser{
		UserName:   userName,
		UserType:   "System",
		UserSource: "#",
	})
	if uniqueId == userId {
		return true
	}
	return uniqueId == 0
}

// CheckUniqueByPhone 检查手机号码是否唯一
func (s SysUser) CheckUniqueByPhone(phone string, userId int64) bool {
	uniqueId := s.sysUserRepository.CheckUnique(model.SysUser{
		Phone:      phone,
		UserType:   "System",
		UserSource: "#",
	})
	if uniqueId == userId {
		return true
	}
	return uniqueId == 0
}

// CheckUniqueByEmail 检查Email是否唯一
func (s SysUser) CheckUniqueByEmail(email string, userId int64) bool {
	uniqueId := s.sysUserRepository.CheckUnique(model.SysUser{
		Email:      email,
		UserType:   "System",
		UserSource: "#",
	})
	if uniqueId == userId {
		return true
	}
	return uniqueId == 0
}

// FindByUserName 通过用户名查询用户信息
// userType 系统sys
// userSource 系统#
func (s SysUser) FindByUserName(userName, userType, userSource string) model.SysUser {
	userinfo := s.sysUserRepository.SelectByUserName(userName, userType, userSource)
	if userinfo.UserName != userName {
		return userinfo
	}
	// 部门
	deptInfo := s.sysDeptRepository.SelectById(userinfo.DeptId)
	userinfo.Dept = &deptInfo
	// 角色
	roleArr := s.sysRoleRepository.SelectByUserId(userinfo.UserId)
	roles := make([]*model.SysRole, 0)
	roleIds := make([]int64, 0)
	for _, role := range roleArr {
		roles = append(roles, &role)
		roleIds = append(roleIds, role.RoleId)
	}
	userinfo.Roles = roles
	userinfo.RoleIds = roleIds
	return userinfo
}

// FindAuthUsersPage 根据条件分页查询分配用户角色列表
func (s SysUser) FindAuthUsersPage(query map[string]string, dataScopeSQL string) ([]model.SysUser, int64) {
	rows, total := s.sysUserRepository.SelectAuthUsersByPage(query, dataScopeSQL)
	for i, v := range rows {
		// 部门
		deptInfo := s.sysDeptRepository.SelectById(v.DeptId)
		rows[i].Dept = &deptInfo
		// 角色
		roleArr := s.sysRoleRepository.SelectByUserId(v.UserId)
		roleIds := make([]int64, 0)
		roles := make([]*model.SysRole, 0)
		for _, role := range roleArr {
			roles = append(roles, &role)
			roleIds = append(roleIds, role.RoleId)
		}
		rows[i].Roles = roles
		rows[i].RoleIds = roleIds
	}
	return rows, total
}

// ValidatePasswordPolicy 判断密码策略强度
func (s SysUser) ValidatePasswordPolicy(password string, errLang string) (bool, string) {
	passwordPolicyStr := s.sysConfigService.FindValueByKey("sys.user.passwordPolicy")
	if passwordPolicyStr == "" {
		// 未配置密码策略
		return false, i18n.TKey(errLang, "config.sys.user.passwordPolicyNot")
	}
	var policy struct {
		MinLength    int `json:"minLength"`
		SpecialChars int `json:"specialChars"`
		Uppercase    int `json:"uppercase"`
		Lowercase    int `json:"lowercase"`
	}

	err := json.Unmarshal([]byte(passwordPolicyStr), &policy)
	if err != nil {
		return false, err.Error()
	}

	errMsg := i18n.TTemplate(errLang, "config.sys.user.passwordPolicyError", map[string]any{
		"minLength":    policy.MinLength,
		"specialChars": policy.SpecialChars,
		"uppercase":    policy.Uppercase,
		"lowercase":    policy.Lowercase,
	})

	if len(password) < policy.MinLength {
		return false, errMsg
	}

	specialChars := len(regexp.MustCompile(`[!@#$%^&*(),.?":{}|<>]`).FindAllString(password, -1))
	if specialChars < policy.SpecialChars {
		return false, errMsg
	}

	uppercase := len(regexp.MustCompile(`[A-Z]`).FindAllString(password, -1))
	if uppercase < policy.Uppercase {
		return false, errMsg
	}

	lowercase := len(regexp.MustCompile(`[a-z]`).FindAllString(password, -1))
	if lowercase < policy.Lowercase {
		return false, errMsg
	}
	return true, ""
}

// ValidatePasswordExpireTime 密码过期时间
func (s SysUser) ValidatePasswordExpireTime(passwordUpdateTime int64) (bool, error) {
	passwdExpireStr := s.sysConfigService.FindValueByKey("sys.user.passwdExpire")
	if passwdExpireStr == "" {
		return false, nil
	}
	var expire struct {
		ExpHours   int `json:"expHours"`
		AlertHours int `json:"alertHours"`
	}
	err := json.Unmarshal([]byte(passwdExpireStr), &expire)
	if err != nil {
		return false, err
	}
	if expire.ExpHours <= 0 {
		return false, nil
	}

	if passwordUpdateTime <= 1e9 {
		return false, fmt.Errorf("login.errPasswdExpire")
	}
	// 计算时间差
	lastUpdateTime := time.UnixMilli(passwordUpdateTime)
	now := time.Now()
	remainingHour := now.Sub(lastUpdateTime).Hours()
	alertFlag := remainingHour > float64(expire.AlertHours)
	if remainingHour > float64(expire.ExpHours) {
		return alertFlag, fmt.Errorf("login.errPasswdExpire")
	}
	return alertFlag, nil
}

// ValidatePasswordNotAllowedHistory 密码不允许使用最近修改的
func (s SysUser) ValidatePasswordNotAllowedHistory(userId int64, password string) error {
	passwdNotAllowedHistoryStr := s.sysConfigService.FindValueByKey("sys.user.passwdNotAllowedHistory")
	if passwdNotAllowedHistoryStr == "" {
		return nil
	}
	passwdNotAllowedHistory := parse.Number(passwdNotAllowedHistoryStr)
	if passwdNotAllowedHistory <= 0 {
		return nil
	}
	// 查询密码历史记录
	rows := s.sysLogUserPasswordService.sysLogUserPasswordRepository.SelectByUserId(userId, int(passwdNotAllowedHistory))
	if len(rows) <= 0 {
		return nil
	}
	// 检查密码是否在历史记录中
	for _, v := range rows {
		compare := crypto.BcryptCompare(password, v.Password)
		if compare {
			return fmt.Errorf("login.errPasswdHistory")
		}
	}
	return nil
}
