package service

import (
	"errors"
	"fmt"
	"strings"

	"github.com/bytedance/sonic"
	"github.com/go-admin-team/go-admin-core/logger"
	"github.com/go-admin-team/go-admin-core/sdk/service"
	"gorm.io/gorm"

	"go-admin/app/admin/models"
	"go-admin/app/admin/service/dto"
	"go-admin/common/actions"
	"go-admin/common/const/rediskey"
	cDto "go-admin/common/dto"
	"go-admin/common/global"
	"go-admin/common/helper"
	"go-admin/models/binancedto"
	"go-admin/services/excservice"
)

type LineApiUser struct {
	service.Service
}

// GetPage 获取LineApiUser列表
func (e *LineApiUser) GetPage(c *dto.LineApiUserGetPageReq, p *actions.DataPermission, list *[]models.LineApiUser, count *int64) error {
	var err error
	var data models.LineApiUser
	query := e.Orm.Model(&data).
		Scopes(
			cDto.MakeCondition(c.GetNeedSearch()),
			cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
			actions.Permission(data.TableName(), p),
		)

	if c.ExchangeType != "" {
		query.Where("exchange_type = ?", c.ExchangeType)
	}

	if c.OpenStatus != nil {
		query.Where("open_status = ?", c.OpenStatus)
	}

	err = query.
		Find(list).Limit(-1).Offset(-1).
		Count(count).Error
	if err != nil {
		e.Log.Errorf("LineApiUserService GetPage error:%s \r\n", err)
		return err
	}

	var userSub binancedto.UserSubscribeState
	for index := range *list {
		val, _ := helper.DefaultRedis.GetString(fmt.Sprintf(global.USER_SUBSCRIBE, (*list)[index].ApiKey))

		if val != "" {
			sonic.Unmarshal([]byte(val), &userSub)

			if userSub.FuturesLastTime != nil {
				(*list)[index].FuturesLastTime = userSub.FuturesLastTime.Format("2006-01-02 15:04:05")
			}

			if userSub.SpotLastTime != nil {
				(*list)[index].SpotLastTime = userSub.SpotLastTime.Format("2006-01-02 15:04:05")
			}
		}
	}
	return nil
}

// Get 获取LineApiUser对象
func (e *LineApiUser) Get(d *dto.LineApiUserGetReq, p *actions.DataPermission, model *models.LineApiUser) error {
	var data models.LineApiUser

	err := e.Orm.Model(&data).
		Scopes(
			actions.Permission(data.TableName(), p),
		).
		First(model, d.GetId()).Error
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		err = errors.New("查看对象不存在或无权查看")
		e.Log.Errorf("Service GetLineApiUser error:%s \r\n", err)
		return err
	}
	if err != nil {
		e.Log.Errorf("db error:%s", err)
		return err
	}
	return nil
}

// Insert 创建LineApiUser对象
func (e *LineApiUser) Insert(c *dto.LineApiUserInsertReq) error {
	var err error
	var data models.LineApiUser
	c.Generate(&data)
	err = e.Orm.Create(&data).Error
	if err != nil {
		e.Log.Errorf("LineApiUserService Insert error:%s \r\n", err)
		return err
	}

	e.saveCache(data)
	val, _ := sonic.MarshalString(&data)

	if val != "" {
		if err := helper.DefaultRedis.RPushList(rediskey.ApiUserActiveList, val); err != nil {
			logger.Error("添加待连接队列失败")
		}
	}

	return nil
}

/*
重启ws
*/
func (e *LineApiUser) restartWebsocket(data models.LineApiUser) {
	socket, ok := excservice.SpotSockets[data.ApiKey]

	if ok && socket != nil {
		socket.Stop()
	}

	fuSocket, ok := excservice.FutureSockets[data.ApiKey]

	if ok && fuSocket != nil {
		fuSocket.Stop()
	}

	e.saveCache(data)

	OpenUserBinanceWebsocket(data)
}

// 保存缓存
func (e *LineApiUser) saveCache(data models.LineApiUser) {
	val, _ := sonic.MarshalString(&data)

	if val != "" {
		key := fmt.Sprintf(rediskey.API_USER, data.Id)

		if err := helper.DefaultRedis.SetString(key, val); err != nil {
			e.Log.Error("缓存api user失败:", err)
		}
	}
}

/*
打开用户websocket订阅
*/
func OpenUserBinanceWebsocket(item models.LineApiUser) {
	proxyType := ""
	ipAddress := ""
	ips := strings.Split(strings.ReplaceAll(item.IpAddress, "：", ":"), "://")

	if len(ips) == 2 {
		proxyType = ips[0]

		if item.UserPass != "" {
			ipAddress = fmt.Sprintf("%s@%s", item.UserPass, ips[1])
		} else {
			ipAddress = ips[1]
		}
	}

	//现货
	if wm, ok := excservice.SpotSockets[item.ApiKey]; ok {
		wm.Restart(item.ApiKey, item.ApiSecret, proxyType, ipAddress)
	} else {
		spotSocket := excservice.NewBinanceWebSocketManager(0, item.ApiKey, item.ApiSecret, proxyType, ipAddress)

		spotSocket.Start()
		excservice.SpotSockets[item.ApiKey] = spotSocket
	}

	if wm, ok := excservice.FutureSockets[item.ApiKey]; ok {
		wm.Restart(item.ApiKey, item.ApiSecret, proxyType, ipAddress)
	} else {
		//合约
		futureSocket := excservice.NewBinanceWebSocketManager(1, item.ApiKey, item.ApiSecret, proxyType, ipAddress)

		futureSocket.Start()
		excservice.FutureSockets[item.ApiKey] = futureSocket
	}
}

// Update 修改LineApiUser对象
func (e *LineApiUser) Update(c *dto.LineApiUserUpdateReq, p *actions.DataPermission) error {
	var err error
	var data = models.LineApiUser{}

	e.Orm.Scopes(
		actions.Permission(data.TableName(), p),
	).First(&data, c.GetId())
	oldApiKey := data.ApiKey

	c.Generate(&data)

	updateGroups, err := e.GetUpdateGroups(c.Id)

	//事务
	err = e.Orm.Transaction(func(tx *gorm.DB) error {
		db := tx.Save(&data)
		if err = db.Error; err != nil {
			e.Log.Errorf("LineApiUserService Save error:%s \r\n", err)
			return err
		}
		if db.RowsAffected == 0 {
			return errors.New("无权更新该数据")
		}

		if len(updateGroups) > 0 {
			if err := tx.Save(&updateGroups).Error; err != nil {
				return err
			}
		}

		return nil
	})

	if err != nil {
		return err
	}

	e.saveCache(data)

	//旧key和新的key不一样，则关闭旧的websocket
	if oldApiKey != data.ApiKey {
		if err := helper.DefaultRedis.RPushList(rediskey.ApiUserDeleteList, oldApiKey); err != nil {
			logger.Error("写入待关闭websocket api key 失败:", err)
		}
	}

	val, _ := sonic.MarshalString(&data)

	if val != "" {
		if err := helper.DefaultRedis.RPushList(rediskey.ApiUserActiveList, val); err != nil {
			logger.Error("写入待激活websocket api key 失败:", err)
		}
	}

	return nil
}

func (e *LineApiUser) GetUpdateGroups(apiUserId int) ([]models.LineApiUserGroup, error) {
	updateGroups := make([]models.LineApiUserGroup, 0)
	var groups []models.LineApiUserGroup
	if err := e.Orm.Model(models.LineApiUserGroup{}).Where(" concat(',',user_id,',') like ? ", fmt.Sprintf("%%,%v,%%", apiUserId)).Find(&groups).Error; err != nil {
		return nil, err
	}

	delId := fmt.Sprintf(",%v,", apiUserId)
	for _, group := range groups {

		userId := strings.ReplaceAll(("," + group.UserId + ","), delId, "")
		group.UserId = strings.Trim(userId, ",")

		updateGroups = append(updateGroups, group)
	}
	return updateGroups, nil
}

// Remove 删除LineApiUser
func (e *LineApiUser) Remove(d *dto.LineApiUserDeleteReq, p *actions.DataPermission) error {
	var data models.LineApiUser

	db := e.Orm.Model(&data).
		Scopes(
			actions.Permission(data.TableName(), p),
		).Delete(&data, d.GetId())
	if err := db.Error; err != nil {
		e.Log.Errorf("Service RemoveLineApiUser error:%s \r\n", err)
		return err
	}
	if db.RowsAffected == 0 {
		return errors.New("无权删除该数据")
	}

	//移除缓存
	delKeys := make([]string, 0)
	for _, id := range d.Ids {
		key := fmt.Sprintf(rediskey.API_USER, id)
		delKeys = append(delKeys, key)
	}

	_, err := helper.DefaultRedis.BatchDeleteKeys(delKeys)

	if err != nil {
		e.Log.Error("批量删除api_user key失败", err)
	}

	return nil
}

// Bind 绑定从属关系
func (e *LineApiUser) Bind(req *dto.LineApiUserBindSubordinateReq, p *actions.DataPermission) error {
	split := strings.Split(req.UserIdStr, ",")
	if len(split) != 2 {
		return errors.New("绑定关系必须是两个账号")
	}

	for _, s := range split {
		var apiUserinfo models.LineApiUser
		e.Orm.Model(&models.LineApiUser{}).Where("id = ? AND subordinate <> '0'", s).Find(&apiUserinfo)
		if apiUserinfo.Id > 0 {
			return errors.New("该用户已经绑定关系")
		}
	}

	group := models.LineApiGroup{
		GroupName: req.GroupName,
		ApiUserId: req.UserIdStr,
	}

	err := e.Orm.Transaction(func(tx *gorm.DB) error {

		err := tx.Model(&models.LineApiGroup{}).Create(&group).Error
		if err != nil {
			return err
		}

		for i, s := range split {
			if i == 0 {
				err = e.Orm.Model(&models.LineApiUser{}).Where("id = ?", s).Updates(map[string]interface{}{
					"subordinate": "1",
					"group_id":    group.Id,
				}).Error
				if err != nil {
					return err
				}
			} else {
				err = e.Orm.Model(&models.LineApiUser{}).Where("id = ?", s).Updates(map[string]interface{}{
					"subordinate": "2",
					"group_id":    group.Id,
				}).Error
				if err != nil {
					return err
				}
			}
		}
		return nil

	})
	if err != nil {
		return errors.New("操作失败")
	}

	SetApiGroupCache(group)
	return nil
}

// GetUser 获取未绑定的用户列表
func (e *LineApiUser) GetUser(req *dto.LineApiUserBindSubordinateReq, list *[]models.LineApiUser) error {
	var err error
	var data models.LineApiUser
	query := e.Orm.Model(&data).Where("subordinate = '0'")

	if req.ExchangeType != "" {
		query = query.Where("exchange_type = ?", req.ExchangeType)
	}

	err = query.Find(list).Error
	if err != nil {
		e.Log.Errorf("LineApiUserService error:%s \r\n", err)
		return err
	}
	return nil
}

// GetMainUser 获取主账号
func (e *LineApiUser) GetMainUser(req *dto.GetMainUserReq, list *[]models.LineApiUser) error {
	var err error
	var data models.LineApiUser
	query := e.Orm.Model(&data).Where("subordinate = '1' AND group_id > 0")

	if req.ExchangeType != "" {
		query = query.Where("exchange_type =?", req.ExchangeType)
	}
	err = query.Find(list).Error
	if err != nil {
		e.Log.Errorf("LineApiUserService error:%s \r\n", err)
		return err
	}
	return nil
}

// InitCache 初始化缓存
func (e *LineApiUser) InitCache() error {
	var items []models.LineApiUser

	if err := e.Orm.Model(&models.LineApiUser{}).Find(&items).Error; err != nil {
		e.Log.Errorf("LineApiUserService error:%s \r\n", err)
		return err
	}

	for _, item := range items {
		e.saveCache(item)
	}

	return nil
}

// GetActiveApis 获取激活的api
func (e *LineApiUser) GetActiveApis(apiIds []int) ([]int, error) {
	result := make([]int, 0)

	if err := e.Orm.Model(&models.LineApiUser{}).Where("open_status = 1 and id IN ?", apiIds).Pluck("id", &result).Error; err != nil {
		return result, err
	}

	return result, nil
}
