package service

import (
	"context"
	"errors"
	"regexp"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
	mhayaTime "github.com/mhaya/extend/time"
	"github.com/mhaya/game/game_cluster/internal/code"
	"github.com/mhaya/game/game_cluster/internal/constant"
	"github.com/mhaya/game/game_cluster/internal/mdb"
	"github.com/mhaya/game/game_cluster/internal/mdb/models"
	"github.com/mhaya/game/game_cluster/nodes/webadmin/common"
	"github.com/mhaya/game/game_cluster/nodes/webadmin/entity"
	"github.com/mhaya/game/game_cluster/nodes/webadmin/model"
	mhayaLogger "github.com/mhaya/logger"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"golang.org/x/crypto/bcrypt"
)

type Admin struct {
	db *mongo.Database
}

func NewAdmin() *Admin {
	return &Admin{
		db: mdb.MDB,
	}
}

func (a *Admin) GetDB() *mongo.Database {
	return a.db
}

func (a *Admin) GetDBName() string {
	return "admin"
}

func CheckPasswordHash(password, hash string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
	return err == nil
}

// HashPassword 加密密码
func HashPassword(password string) (string, error) {
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
	return string(bytes), err
}

// Login 登录
func (a *Admin) Login(ctx *gin.Context, username string, password string) (*entity.AdminResp, *code.Result) {
	user, err := a.QueryUserByUsername(ctx, username)
	if err != nil {
		mhayaLogger.Warnf("Login QueryUserByUsername error:%v", err)
		if errors.Is(err, mongo.ErrNoDocuments) {
			return nil, common.NewResult(code.AccountNotExistError)
		}

		return nil, common.NewResult(code.InternalError)
	}

	// 判断用户状态
	if user.Status != 1 {
		return nil, common.NewResult(code.DisabledUserError)
	}

	// 判断密码
	if !CheckPasswordHash(password, user.Password) {
		return nil, common.NewResult(code.UserNameOrPasswordError)
	}

	token, err := mdb.RDB.Get(ctx, user.Username).Result()
	if err != nil && err != redis.Nil {
		mhayaLogger.Warnf("Login Get error:%v", err)
		return nil, common.NewResult(code.InternalError)
	}

	if token == "" {
		// 创建token
		generateToken, err := user.GenerateToken()
		if err != nil {
			mhayaLogger.Warnf("Login GenerateToken error:%v", err)
			return nil, common.NewResult(code.InternalError)
		}

		// 保存username -> token 到 redis 中 过期时间为1天
		err = mdb.RDB.Set(ctx, user.Username, generateToken, 24*time.Hour).Err()
		if err != nil {
			mhayaLogger.Warnf("Login Set user.Username error:%v", err)
			return nil, common.NewResult(code.InternalError)
		}

		// 保存token -> Username 到 redis 中 过期时间为1天
		err = mdb.RDB.Set(ctx, common.GetUserNameKey(generateToken), user.Username, 24*time.Hour).Err()
		if err != nil {
			mhayaLogger.Warnf("Login Set generateToken Username error:%v", err)
			return nil, common.NewResult(code.InternalError)
		}

		// 保存token -> RoleId 到 redis 中 过期时间为1天
		err = mdb.RDB.Set(ctx, generateToken, user.RoleId, 24*time.Hour).Err()
		if err != nil {
			mhayaLogger.Warnf("Login Set generateToken RoleId error:%v", err)
			return nil, common.NewResult(code.InternalError)
		}

		err = a.loginAuthSetRoleRedis(user.RoleId, generateToken)
		if err != nil {
			mhayaLogger.Warnf("Login loginAuthSetRoleRedis error:%v", err)
			return nil, common.NewResult(code.InternalError)
		}

		token = generateToken
	}

	// 更新用户登录时间
	_, err = mdb.MDB.Collection(a.GetDBName()).UpdateOne(ctx, bson.M{"username": username}, bson.M{"$set": bson.M{"last_login_time": time.Now().Unix()}})
	if err != nil {
		mhayaLogger.Warnf("Login UpdateOne last_login_time error:%v", err)
		return nil, common.NewResult(code.InternalError)
	}

	// 更新用户IP
	ip := ctx.ClientIP()
	_, err = mdb.MDB.Collection(a.GetDBName()).UpdateOne(ctx, bson.M{"username": username}, bson.M{"$set": bson.M{"last_login_ip": ip}})
	if err != nil {
		mhayaLogger.Warnf("Login UpdateOne ip error:%v", err)
		return nil, common.NewResult(code.InternalError)
	}

	// 返回用户信息
	resp := &entity.AdminResp{
		ToKen:  token,
		RoleID: user.RoleId,
	}

	return resp, nil
}

// LoginAuthSetRoleRedis 登录时写入该用户的权限
func (a *Admin) loginAuthSetRoleRedis(roleID, generateToken string) error {
	if roleID == constant.AdminAccess {
		mdb.RDB.HSet(context.Background(), common.GetTokenKey(generateToken), constant.AdminAccess, 1)
		mdb.RDB.Expire(context.Background(), common.GetTokenKey(generateToken), 24*time.Hour).Err()
		return nil
	}

	// 写入redis
	role := models.Roles{}
	collection := mdb.MDB.Collection(role.TableName())
	roleIdObj, _ := primitive.ObjectIDFromHex(roleID)
	filter := bson.M{"_id": roleIdObj, "status": 1}
	err := collection.FindOne(context.TODO(), filter).Decode(&role)
	if err != nil {
		return err
	}

	roleAccess := models.RoleAccess{}
	collection = mdb.MDB.Collection(roleAccess.TableName())
	roleAccessFilter := bson.M{"role_id": roleID}
	err = collection.FindOne(context.TODO(), roleAccessFilter).Decode(&roleAccess)
	if err != nil {
		return err
	}

	// 写入redis
	var accessIDS []primitive.ObjectID
	for _, v := range roleAccess.AccessID {
		accessIdObj, _ := primitive.ObjectIDFromHex(v)
		accessIDS = append(accessIDS, accessIdObj)
	}

	access := models.Access{}
	var accessList []models.Access
	collection = mdb.MDB.Collection(access.TableName())
	accessFilter := bson.M{"_id": bson.M{"$in": accessIDS}}
	cursor, err := collection.Find(context.Background(), accessFilter)
	if err != nil {
		return err
	}

	if err = cursor.All(context.Background(), &accessList); err != nil {
		return err
	}

	for _, v := range accessList {
		mdb.RDB.HSet(context.Background(), common.GetTokenKey(generateToken), v.URL, 1)
	}
	mdb.RDB.Expire(context.Background(), common.GetTokenKey(generateToken), 24*time.Hour).Err()

	return nil

}

// QueryUserByUsername 根据用户名查询用户
func (a *Admin) QueryUserByUsername(ctx context.Context, username string) (*model.Admin, error) {
	admin := &model.Admin{}
	err := mdb.MDB.Collection(a.GetDBName()).FindOne(ctx, bson.M{"username": username}).Decode(&admin)
	if errors.Is(err, mongo.ErrNoDocuments) {
		if username != "admin" {
			return nil, err
		}

		// 如果是admin 登录的话 创建一个初始的admin并且存入数据库
		pwd, err := HashPassword("123456")
		if err != nil {
			return nil, err
		}
		admin = &model.Admin{
			Username:      constant.AdminAccess,
			Password:      pwd,
			RealName:      constant.AdminAccess,
			Pid:           "0",
			RoleId:        constant.AdminAccess,
			ManagerAuth:   0,
			Status:        1,
			CreatedAt:     mhayaTime.Now().Unix(),
			UpdatedAt:     0,
			DeletedAt:     0,
			LastLoginIp:   "",
			LastLoginTime: 0,
		}
		_, err = mdb.MDB.Collection(a.GetDBName()).InsertOne(ctx, bson.M{
			"username":  constant.AdminAccess,
			"password":  pwd,
			"real_name": constant.AdminAccess,
			"pid":       "0",
			"role_id":   constant.AdminAccess,
			"status":    1,
		})
		if err != nil {
			return nil, err
		}
		return admin, nil
	}
	if err != nil {
		return nil, err
	}

	return admin, nil
}

// ChangePassword 修改管理员密码
func (a *Admin) ChangePassword(ctx context.Context, username string, password string) *code.Result {
	// 更新密码
	newPassword, err := HashPassword(password)
	if err != nil {
		mhayaLogger.Warnf("ChangePassword HashPassword error:%v", err)
		return common.NewResult(code.InternalError)
	}

	_, err = mdb.MDB.Collection(a.GetDBName()).UpdateOne(ctx, bson.M{"username": username}, bson.M{"$set": bson.M{"password": newPassword}})
	if err != nil {
		mhayaLogger.Warnf("ChangePassword UpdateOne error:%v", err)
		return common.NewResult(code.InternalError)
	}

	return nil
}

// Add 添加管理员
func (a *Admin) Add(ctx context.Context, username string, password string, realName string, pid string, roleId string, status int) *code.Result {
	// 验证角色名称是否已存在
	exist, err := NewRole().CheckRoleNameExist(roleId)
	if err != nil {
		mhayaLogger.Warnf("Add CheckRoleNameExist error:%v", ctx.Err())
		return common.NewResult(code.InternalError)
	}
	if !exist {
		return common.NewResult(code.RoleNameNotExistError)
	}

	// 判断账号是否重复
	admin := model.Admin{}
	err = mdb.MDB.Collection(a.GetDBName()).FindOne(ctx, bson.M{"username": username}).Decode(&admin)
	if errors.Is(err, mongo.ErrNoDocuments) {
		newPassword, err := HashPassword(password)
		if err != nil {
			mhayaLogger.Warnf("Add HashPassword error:%v", err)
			return common.NewResult(code.InternalError)
		}

		_, err = mdb.MDB.Collection(a.GetDBName()).InsertOne(ctx, bson.M{
			"username":   username,
			"password":   newPassword,
			"real_name":  realName,
			"pid":        pid,
			"role_id":    roleId,
			"status":     status,
			"created_at": time.Now().Unix(),
			"updated_at": time.Now().Unix(),
		})
		if err != nil {
			mhayaLogger.Warnf("Add InsertOne error:%v", err)
			return common.NewResult(code.InternalError)
		}

		return nil
	}

	return common.NewResult(code.AccountExistError)
}

// Delete 删除管理员
func (a *Admin) Delete(ctx context.Context, username string) *code.Result {
	_, err := mdb.MDB.Collection(a.GetDBName()).DeleteOne(ctx, bson.M{"username": username})
	if err != nil {
		mhayaLogger.Warnf("Delete DeleteOne error:%v", err)
		return common.NewResult(code.InternalError)
	}

	return nil
}

// UpdateStatus updateStatus
func (a *Admin) UpdateStatus(ctx context.Context, username string, status int) *code.Result {
	_, err := mdb.MDB.Collection(a.GetDBName()).UpdateOne(ctx, bson.M{"username": username}, bson.M{"$set": bson.M{"status": status}})
	if err != nil {
		mhayaLogger.Warnf("Delete UpdateOne error:%v", err)
		return common.NewResult(code.InternalError)
	}

	return nil
}

// FindAll 查找所有管理员信息
func (a *Admin) FindAll(ctx context.Context, req entity.AdminFindAllReq) (*entity.AdminListResp, *code.Result) {
	// 验证参数
	page, pageSize := checkPageParam(req.Page, req.Size)

	// 构建查询条件
	filter := bson.M{}
	if req.Username != "" {
		filter["username"] = bson.M{"$regex": escapeRegex(req.Username), "$options": "i"}
	}
	if req.RoleId != "" {
		filter["role_id"] = req.RoleId
	}
	// 查询总数
	count, err := mdb.MDB.Collection("admin").CountDocuments(ctx, filter)
	if err != nil {
		mhayaLogger.Warnf("FindAll CountDocuments error:%v", err)
		return nil, common.NewResult(code.InternalError)
	}

	// 设置分页选项
	skip := (page - 1) * pageSize
	limit := pageSize
	findOptions := options.Find().SetSkip(int64(skip)).SetLimit(int64(limit))

	// 执行查询
	cursor, err := mdb.MDB.Collection("admin").Find(ctx, filter, findOptions)
	if err != nil {
		mhayaLogger.Warnf("FindAll Find error:%v", err)
		return nil, common.NewResult(code.InternalError)
	}
	defer func() {
		if closeErr := cursor.Close(ctx); closeErr != nil {
			mhayaLogger.Warnf("Error closing cursor: %v", closeErr)
		}
	}()

	// 解析结果
	admins := make([]*model.Admin, 0)
	for cursor.Next(ctx) {
		var admin model.Admin
		err := cursor.Decode(&admin)
		if err != nil {
			mhayaLogger.Warnf("FindAll Decode error:%v", err)
			return nil, common.NewResult(code.InternalError)
		}
		admins = append(admins, &admin)
	}

	if err := cursor.Err(); err != nil {
		mhayaLogger.Warnf("FindAll cursor error:%v", err)
		return nil, common.NewResult(code.InternalError)
	}

	var details []*entity.AdminListDetail
	for _, admin := range admins {
		roleName := ""
		roleName, _ = a.GetRoleName(admin.RoleId)

		ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
		defer cancel()

		roleAccessDetails, err := NewRole().GetRoleAccessList(ctx, entity.RoleAccessListReq{
			RoleId: admin.RoleId,
		})
		if err != nil {
			mhayaLogger.Warnf("FindAll GetRoleAccessList error:%v", err)
			return nil, err
		}

		details = append(details, &entity.AdminListDetail{
			Id:            admin.GetID(),
			Username:      admin.Username,
			RealName:      admin.RealName,
			RoleId:        admin.RoleId,
			RoleName:      roleName,
			Status:        admin.Status,
			CreatedAt:     admin.CreatedAt,
			UpdatedAt:     admin.UpdatedAt,
			LastLoginIp:   admin.LastLoginIp,
			LastLoginTime: admin.LastLoginTime,
			AccessDetails: func() []*entity.AccessDetail {
				if roleAccessDetails != nil {
					return roleAccessDetails.Details
				}

				return nil
			}(),
		})
	}

	return &entity.AdminListResp{
		Details: details,
		Total:   count,
	}, nil
}

func (a *Admin) GetRoleName(roleID string) (string, error) {
	var role models.Roles
	err := mdb.MDB.Collection(role.TableName()).FindOne(context.Background(), bson.M{"name": roleID}).Decode(&role)
	return role.Name, err
}

// GetServerStatus 获取服务器状态
func (a *Admin) GetServerStatus(ctx context.Context) ([]*models.PlayerServerLoadStat, *code.Result) {
	// 执行查询
	cursor, err := mdb.MDB.Collection(constant.CNameServerLoadStat).Find(ctx, bson.M{})
	if err != nil {
		mhayaLogger.Warnf("GetServerStatus Find error:%v", err)
		return nil, common.NewResult(code.InternalError)
	}

	defer func() {
		if closeErr := cursor.Close(ctx); closeErr != nil {
			mhayaLogger.Warnf("Error closing cursor: %v", closeErr)
		}
	}()

	// 解析结果
	admins := make([]*models.PlayerServerLoadStat, 0)
	for cursor.Next(ctx) {
		var admin models.PlayerServerLoadStat
		err := cursor.Decode(&admin)
		if err != nil {
			mhayaLogger.Warnf("GetServerStatus Decode error:%v", err)
			return nil, common.NewResult(code.InternalError)
		}
		admins = append(admins, &admin)
	}

	if err := cursor.Err(); err != nil {
		mhayaLogger.Warnf("GetServerStatus cursor error:%v", err)
		return nil, common.NewResult(code.InternalError)
	}

	return admins, nil
}

// 辅助函数：对 username 进行脱敏处理
func maskUsername(username string) string {
	if username == "" {
		return ""
	}

	return strings.Repeat("*", len(username))
}

// 辅助函数：对正则表达式进行转义
func escapeRegex(s string) string {
	return regexp.QuoteMeta(s)
}
