package jobs

import (
	"errors"
	"fmt"
	"go-admin/app/admin/models"
	"go-admin/app/admin/service"
	"go-admin/app/admin/service/dto"
	"go-admin/common/const/rediskey"
	"go-admin/common/global"
	"go-admin/common/helper"
	"go-admin/models/positiondto"
	"go-admin/pkg/utility"
	"time"

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

// InitJob
// 需要将定义的struct 添加到字典中；
// 字典 key 可以配置到 自动任务 调用目标 中；
func InitJob() {
	jobList = map[string]JobExec{
		"ExamplesOne": ExamplesOne{},
		// ...
		"DeleteExpireOrder":              DeleteExpireOrder{},              //定时删除过期数据
		"UpRange":                        UpRange{},                        //更新涨跌幅
		"InitFuturesSymbol":              InitFuturesSymbol{},              //定时更新合约交易对
		"InitSpotSymbol":                 InitSpotSymbol{},                 //定时更新现货交易对
		"ClearLogJob":                    ClearLogJob{},                    //定时清理日志
		"AutoPlaceOrder":                 AutoPlaceOrder{},                 //定时下单
		"LimitOrderTimeoutDuration":      LimitOrderTimeoutDuration{},      //定时取消限价单改下市价单
		"ListenSymbol":                   ListenSymbol{},                   //交易对监听
		"MemberExpirationJob":            MemberExpirationJob{},            //会员到期处理
		"MemberRenwalOrderExpirationJob": MemberRenwalOrderExpirationJob{}, //会员续费订单过期处理
		"TrxQueryJobs":                   TrxQueryJobs{},                   //订单支付监听
	}
}

// ExamplesOne
// 新添加的job 必须按照以下格式定义，并实现Exec函数
type ExamplesOne struct {
}

func (t ExamplesOne) Exec(arg interface{}) error {
	str := time.Now().Format(timeFormat) + " [INFO] JobCore ExamplesOne exec success"
	// TODO: 这里需要注意 Examples 传入参数是 string 所以 arg.(string)；请根据对应的类型进行转化；
	switch arg.(type) {

	case string:
		if arg.(string) != "" {
			fmt.Println("string", arg.(string))
			fmt.Println(str, arg.(string))
		} else {
			fmt.Println("arg is nil")
			fmt.Println(str, "arg is nil")
		}
		break
	}

	return nil
}

// DeleteExpireOrder 清理过期订单
type DeleteExpireOrder struct {
}

func (receiver DeleteExpireOrder) Exec(arg interface{}) error {
	dbs := sdk.Runtime.GetDb()
	var db *gorm.DB

	for _, item := range dbs {
		db = item
		break
	}
	orders := make([]models.LinePreOrder, 0)
	err := db.Model(&models.LinePreOrder{}).Where("status = '0' AND expire_time <= ? AND order_type = 0 AND pid=0", time.Now()).Find(&orders).Error
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return err
	}
	for _, order := range orders {
		//删除止盈止损数据
		db.Model(&models.LinePreOrder{}).Where("pid = ?", order.Id).Unscoped().Delete(&models.LinePreOrder{})
		//删除缓存
		list := dto.PreOrderRedisList{
			Id:          order.Id,
			Symbol:      order.Symbol,
			Price:       order.Price,
			Site:        order.Site,
			ApiId:       order.ApiId,
			OrderSn:     order.OrderSn,
			QuoteSymbol: order.QuoteSymbol,
		}
		marshal, _ := sonic.Marshal(&list)
		if order.SymbolType == 1 {
			helper.DefaultRedis.LRem(fmt.Sprintf(rediskey.PreSpotOrderList, order.ExchangeType), string(marshal))
		} else {
			helper.DefaultRedis.LRem(fmt.Sprintf(rediskey.PreFutOrderList, order.ExchangeType), string(marshal))
		}
		//删除主单
		db.Model(&models.LinePreOrder{}).Where("id = ?", order.Id).Unscoped().Delete(&models.LinePreOrder{})
	}

	//子订单 加仓、检查过期
	childOrders := make([]models.LinePreOrder, 0)
	err = db.Model(&models.LinePreOrder{}).Where("pid >0 AND order_type IN (0,4) AND status=0 AND expire_time <= ?", time.Now()).Select("id", "order_category", "order_type").Find(&childOrders).Error

	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return err
	}

	removeExpirateChildOrder(childOrders, db)

	return nil
}

func removeExpirateChildOrder(childOrders []models.LinePreOrder, db *gorm.DB) {
	childOrderId := make([]int, 0)
	futAddKey := fmt.Sprintf(rediskey.FuturesAddPositionList, global.EXCHANGE_BINANCE)
	spotAddKey := fmt.Sprintf(rediskey.SpotAddPositionList, global.EXCHANGE_BINANCE)
	futReduceKey := fmt.Sprintf(rediskey.FuturesReduceList, global.EXCHANGE_BINANCE)
	spotReduceKey := fmt.Sprintf(rediskey.SpotReduceList, global.EXCHANGE_BINANCE)
	futAddPositionStr, _ := helper.DefaultRedis.GetAllList(futAddKey)
	spotAdPositionStr, _ := helper.DefaultRedis.GetAllList(spotAddKey)
	futReduceStr, _ := helper.DefaultRedis.GetAllList(futReduceKey)
	spotReduceStr, _ := helper.DefaultRedis.GetAllList(spotReduceKey)
	futAddPositionCache := positiondto.AddPositionList{}
	spotAddPositionCache := positiondto.AddPositionList{}
	futReduceCache := positiondto.ReduceListItem{}
	spotReduceCache := positiondto.ReduceListItem{}
	futAddPositionMap := map[int]string{}
	spotAddPositionMap := map[int]string{}
	futReduceMap := map[int]string{}
	spotReduceMap := map[int]string{}

	for _, item := range futAddPositionStr {
		sonic.Unmarshal([]byte(item), &futAddPositionCache)
		futAddPositionMap[futAddPositionCache.Id] = item
	}

	for _, item := range spotAdPositionStr {
		sonic.Unmarshal([]byte(item), &spotAddPositionCache)
		spotAddPositionMap[spotAddPositionCache.Id] = item
	}

	for _, item := range futReduceStr {
		sonic.Unmarshal([]byte(item), &futReduceCache)
		futReduceMap[futReduceCache.Id] = item
	}

	for _, item := range spotReduceStr {
		sonic.Unmarshal([]byte(item), &spotReduceCache)
		spotReduceMap[spotReduceCache.Id] = item
	}

	for _, childOrder := range childOrders {
		switch {
		//加仓单
		case childOrder.OrderType == 0 && childOrder.OrderCategory == 3:
			if val, ok := futAddPositionMap[childOrder.Id]; ok {
				helper.DefaultRedis.LRem(futAddKey, val)
			}

			if val, ok := spotAddPositionMap[childOrder.Id]; ok {
				helper.DefaultRedis.LRem(spotAddKey, val)
			}

			//减仓单
		case childOrder.OrderType == 4 && childOrder.OrderCategory == 1:
			if val, ok := futReduceMap[childOrder.Id]; ok {
				helper.DefaultRedis.LRem(futReduceKey, val)
			}

			if val, ok := spotReduceMap[childOrder.Id]; ok {
				helper.DefaultRedis.LRem(spotReduceKey, val)
			}
		default:
			continue
		}
		childOrderId = append(childOrderId, childOrder.Id)
	}

	idArrays := utility.SplitSlice(childOrderId, 1000)

	for _, idArray := range idArrays {
		if err := db.Delete(&models.LinePreOrder{}, idArray).Error; err != nil {
			logger.Errorf("删除过期子订单失败,err", err)
		}
	}
}

// UpRange 更新涨跌幅
type UpRange struct {
}

func (receiver UpRange) Exec(arg interface{}) error {
	dbs := sdk.Runtime.GetDb()
	var db *gorm.DB

	for _, item := range dbs {
		db = item
		break
	}

	limit := service.LinePriceLimit{
		Service: sysservice.Service{Orm: db},
	}
	err := limit.UpRange()
	if err != nil {
		return err
	}
	return nil
}
