package modelOperator

import (
	"context"
	"fmt"
	"game/common/proto/pb"
	"game/common/serialization"
	"game/common/utils"
	"github.com/fox/fox/log"
	"github.com/go-redis/redis/v8"
	"gorm.io/gorm"
	"reflect"
	"strconv"
	"time"
)

const (
	TableExpire = 7 * 24 * time.Hour // 七天后过期
)

type iTable interface {
	GetId() int64
	GetIdName() string
	TableName() string
}

/*
T:Table，如果不想操作redis则将rds设置为nil
*/
type TableOp[T iTable] struct {
	db  *gorm.DB
	rds *redis.Client
}

func NewTableOp[T iTable](db *gorm.DB, rds *redis.Client) *TableOp[T] {
	return &TableOp[T]{db: db, rds: rds}
}

func (s *TableOp[T]) tableName() string {
	var result utils.TValue[T]
	return result.V.TableName()
}

func (s *TableOp[T]) redisKey(id int64) string {
	return fmt.Sprintf("%s:%d", s.tableName(), id)
}

// 查找并返回结构体
func (s *TableOp[T]) findByRedis(id int64) *T {
	maps := s.findByRedisMaps(id)
	if len(maps) == 0 {
		return nil
	}
	us, err := serialization.MapToStruct[T](maps)
	if err != nil {
		log.ErrorF("serialization map to struct err: %v", err)
		return nil
	}
	//log.DebugF("findByRedis redis-key:%v result:%v", s.redisKey(id), us)
	return us
}

// 查找并返回map
func (s *TableOp[T]) findByRedisMaps(id int64) map[string]string {
	if s.rds == nil {
		return nil
	}
	maps, err := s.rds.HGetAll(context.Background(), s.redisKey(id)).Result()
	if err != nil {
		log.ErrorF("redis-key:%v HGetAll err: %v", s.redisKey(id), err)
		return nil
	}
	if len(maps) == 0 {
		return nil
	}
	return maps
}

func (s *TableOp[T]) writeRedis(id int64, t *T) {
	if s.rds == nil {
		return
	}
	maps := serialization.StructToMap(t)
	if len(maps) == 0 {
		log.ErrorF("table struct is empty:%v", s.tableName())
	}
	s.updateRedis(id, maps)
}

// 查看在redis中是否存在
func (s *TableOp[T]) existRedis(t *T) bool {
	if s.rds == nil {
		return false
	}
	// 查看redis中是否存在该键
	exist, _ := s.rds.Exists(context.Background(), s.redisKey((*t).GetId())).Result()
	return exist == 1
}

func (s *TableOp[T]) updateRedis(id int64, maps map[string]any) {
	if s.rds == nil {
		return
	}
	if err := s.rds.HMSet(context.Background(), s.redisKey(id), maps).Err(); err != nil {
		log.ErrorF("redis-key:%v HMSet err: %v", s.redisKey(id), err)
	}
	_ = s.rds.Expire(context.Background(), s.redisKey(id), TableExpire).Err()
}

func (s *TableOp[T]) deleteRedis(id int64) {
	if s.rds == nil {
		return
	}
	_ = s.rds.Del(context.Background(), s.redisKey(id)).Err()
}

func (s *TableOp[T]) Create(t *T) (*T, pb.ErrCode) {
	if err := s.db.Create(t).Error; err != nil {
		log.ErrorF("create table:%v err:%v", s.tableName(), err)
		return nil, pb.ErrCode_SystemErr
	}
	s.writeRedis((*t).GetId(), t)
	return t, pb.ErrCode_OK
}

func (s *TableOp[T]) Find(id int64) (*T, pb.ErrCode) {
	// 先从redis中查询，redis中没有则从mysql中查询
	if table := s.findByRedis(id); table != nil {
		return table, pb.ErrCode_OK
	}
	var result utils.TValue[T]
	err := s.db.Where(result.V.GetIdName()+" = ?", id).First(&result.V).Error
	if err != nil {
		log.DebugF("find table:%v id:%v err:%v", s.tableName(), id, err)
		return nil, pb.ErrCode_SystemErr
	}
	return &result.V, pb.ErrCode_OK
}

// 根据条件查询，只在mysql中查询，无法在redis中查询
func (s *TableOp[T]) FindCondition(condition map[string]any) (*T, pb.ErrCode) {
	us := new(T)
	err := s.db.Where(condition).First(us).Error
	if err != nil {
		log.ErrorF("find table:%v condition:%v err:%v", s.tableName(), utils.JsonMarshal(condition), err)
		return nil, pb.ErrCode_SystemErr
	}
	// 查看redis中是否存在该键，不存在则写入数据
	if !s.existRedis(us) {
		s.writeRedis((*us).GetId(), us)
	}
	return us, pb.ErrCode_OK
}

func (s *TableOp[T]) Update(id int64, updates map[string]any) (*T, pb.ErrCode) {
	var result utils.TValue[T]
	err := s.db.Model(&result.V).Where(result.V.GetIdName()+" = ?", id).Updates(updates).Error
	if err != nil {
		log.ErrorF("update table:%v id:%v err:%v", s.tableName(), id, err)
		return nil, pb.ErrCode_SystemErr
	}
	s.updateRedis(id, updates)
	return &result.V, pb.ErrCode_OK
}

// 辅助函数：将map的keys转为字符串slice
func keysToStringSlice(m map[string]int64) []string {
	keys := make([]string, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	return keys
}

// 获取资源
func (s *TableOp[T]) GetInt(id int64, resName []string) (map[string]int64, pb.ErrCode) {
	mapFields := s.findByRedisMaps(id)
	// 查询更新后的值到map
	updatedValues := make(map[string]int64)
	if len(mapFields) != 0 {
		for _, name := range resName {
			v, _ := strconv.ParseInt(mapFields[name], 10, 64)
			updatedValues[name] = v
		}
		return updatedValues, pb.ErrCode_OK
	}

	var result utils.TValue[T]
	// redis中没有值，从表中加载并写入redis
	mapAny := make(map[string]any)
	err := s.db.Model(new(T)).
		Where(result.V.GetIdName()+" = ?", id).
		Take(&mapAny). // 扫描到map
		Error

	if err != nil {
		log.ErrorF("query updated values table:%v id:%v err:%v", s.tableName(), id, err)
		return nil, pb.ErrCode_SystemErr
	}
	s.updateRedis(id, mapAny)

	// 查询更新后的值到map
	updatedValues = make(map[string]int64)
	for _, name := range resName {
		if val, ok := mapAny[name]; ok {
			var v64 int64
			switch v := val.(type) {
			case int64:
				v64 = v
			case int, int32, uint, uint32, uint64:
				v64 = reflect.ValueOf(v).Int()
			case float32, float64:
				v64 = int64(reflect.ValueOf(v).Float())
			default:
				// 处理无法转换的情况
			}
			updatedValues[name] = v64
		}
	}
	return updatedValues, pb.ErrCode_OK
}

// 增加或减少资源
func (s *TableOp[T]) AddInt(id int64, res map[string]int64) (map[string]int64, pb.ErrCode) {
	addRes := map[string]any{}
	for k, v := range res {
		addRes[k] = gorm.Expr(fmt.Sprintf("%v + ?", k), v)
	}
	var result utils.TValue[T]
	err := s.db.Model(&result.V).Where(result.V.GetIdName()+" = ?", id).Updates(addRes).Error
	if err != nil {
		log.ErrorF("add table:%v id:%v err:%v", s.tableName(), id, err)
		return nil, pb.ErrCode_SystemErr
	}

	rows, err := s.db.Model(&result.V).
		Select(keysToStringSlice(res)). // 只选择需要返回的字段
		Where(result.V.GetIdName()+" = ?", id).
		Rows()

	if err != nil {
		log.ErrorF("query updated values table:%v id:%v err:%v", s.tableName(), id, err)
		return nil, pb.ErrCode_SystemErr
	}
	defer func() { _ = rows.Close() }() // 确保关闭
	// 查询更新后的值到map
	updatedValues := make(map[string]int64)
	// 手动扫描到 map
	if rows.Next() {
		columns, _ := rows.Columns()
		values := make([]interface{}, len(columns))
		for i := range values {
			values[i] = new(int64) // 假设所有字段都是 int64
		}
		err = rows.Scan(values...)
		if err != nil {
			log.ErrorF("scan updated values err:%v", err)
			return nil, pb.ErrCode_SystemErr
		}
		for i, col := range columns {
			updatedValues[col] = *(values[i].(*int64))
		}
	} else {
		return nil, pb.ErrCode_SystemErr // 无记录
	}

	mapAny := make(map[string]any)
	for k, v := range updatedValues {
		mapAny[k] = v
	}
	s.updateRedis(id, mapAny)
	return updatedValues, pb.ErrCode_OK
}

func (s *TableOp[T]) Delete(id int64) (*T, pb.ErrCode) {
	var result utils.TValue[T]
	err := s.db.Delete(&result.V, id).Error
	if err != nil {
		log.ErrorF("delete table:%v err:%v", s.tableName(), err)
		return nil, pb.ErrCode_SystemErr
	}
	s.deleteRedis(id)
	return &result.V, pb.ErrCode_OK
}
