package protocol

import (
	"errors"
	"fmt"
	"github.com/injoyai/base/types"
	"github.com/injoyai/conv"
	"time"
)

type TradeResp struct {
	Count uint16
	List  Trades
}

// Trade 分时成交，todo 时间没有到秒，客户端上也没有,东方客户端能显示秒
type Trade struct {
	Time   time.Time //时间, 09:30
	Price  Price     //价格
	Volume int       //成交量,手
	Status int       //0是买，1是卖，2中性/汇总 中途也可能出现2,例20241115(sz000001)的14:56
	Number int       //单数,历史数据该字段无效
}

func (this *Trade) String() string {
	return fmt.Sprintf("%s \t%-6s \t%-6s \t%-6d(手) \t%-4d(单) \t%-4s",
		this.Time, this.Price, this.Amount(), this.Volume, this.Number, this.StatusString())
}

// Amount 成交额
func (this *Trade) Amount() Price {
	return this.Price * Price(this.Volume*100)
}

func (this *Trade) StatusString() string {
	switch this.Status {
	case 0:
		return "买入"
	case 1:
		return "卖出"
	default:
		return ""
	}
}

// AvgVolume 平均每单成交量
func (this *Trade) AvgVolume() float64 {
	return float64(this.Volume) / float64(this.Number)
}

// AvgPrice 平均每单成交金额
func (this *Trade) AvgPrice() Price {
	return Price(this.AvgVolume() * float64(this.Price) * 100)
}

// IsBuy 是否是买单
func (this *Trade) IsBuy() bool {
	return this.Status == 0
}

// IsSell 是否是卖单
func (this *Trade) IsSell() bool {
	return this.Status == 1
}

type trade struct{}

func (trade) Frame(code string, start, count uint16) (*Frame, error) {
	exchange, number, err := DecodeCode(code)
	if err != nil {
		return nil, err
	}

	codeBs := []byte(number)
	codeBs = append(codeBs, Bytes(start)...)
	codeBs = append(codeBs, Bytes(count)...)
	return &Frame{
		Control: Control01,
		Type:    TypeMinuteTrade,
		Data:    append([]byte{exchange.Uint8(), 0x0}, codeBs...),
	}, nil
}

func (trade) Decode(bs []byte, c TradeCache) (*TradeResp, error) {

	_, code, err := DecodeCode(c.Code)
	if err != nil {
		return nil, err
	}

	if len(bs) < 2 {
		return nil, errors.New("数据长度不足")
	}

	resp := &TradeResp{
		Count: Uint16(bs[:2]),
	}

	bs = bs[2:]

	lastPrice := Price(0)
	for i := uint16(0); i < resp.Count; i++ {
		timeStr := GetHourMinute([2]byte(bs[:2]))
		t, err := time.Parse("2006010215:04", c.Date+timeStr)
		if err != nil {
			return nil, err
		}
		mt := &Trade{Time: t}
		var sub Price
		bs, sub = GetPrice(bs[2:])
		lastPrice += sub * 10 //把分转换成厘
		mt.Price = lastPrice / basePrice(code)
		bs, mt.Volume = CutInt(bs)
		bs, mt.Number = CutInt(bs)
		bs, mt.Status = CutInt(bs)
		bs, _ = CutInt(bs) //这个得到的是0，不知道是啥
		resp.List = append(resp.List, mt)
	}

	return resp, nil
}

type Trades []*Trade

// Klines 合并分时成交成k线
func (this Trades) Klines() Klines {
	//按天分割
	m := make(types.SortMap[int64, Trades])
	for _, v := range this {
		//获取当天零点的时间戳
		unix := time.Date(v.Time.Year(), v.Time.Month(), v.Time.Day(), 0, 0, 0, 0, v.Time.Location()).Unix()
		m[unix] = append(m[unix], v)
	}

	//按天排序
	mKline := types.SortMap[int64, Klines]{}
	for date, v := range m {
		//生成一分钟k线
		t := time.Unix(date, 0)
		mKline[date] = v.klinesForDay(t)
	}
	//按时间排序
	lss := mKline.Sort()
	ls := Klines{}
	for _, v := range lss {
		ls = append(ls, v...)
	}
	return ls
}

// Kline 合并分时成交成1个k线,注意分时成交时间保持一致
func (this Trades) Kline(t time.Time, last Price) *Kline {
	k := &Kline{
		Time:  t,
		Last:  last,
		Open:  last,
		High:  last,
		Low:   last,
		Close: last,
	}
	first := 0
	for _, v := range this {
		if v.Price <= 0 {
			continue
		}
		switch first {
		case 0:
			k.Open = v.Price
			k.High = v.Price
			k.Low = v.Price
			k.Close = v.Price
		default:
			k.High = conv.Select(k.High < v.Price, v.Price, k.High)
			k.Low = conv.Select(k.Low > v.Price, v.Price, k.Low)
		}
		k.Close = v.Price
		k.Volume += int64(v.Volume)
		k.Order += v.Number
		k.Amount += v.Price * Price(v.Volume) * 100
		first++
	}
	return k
}

// kline1 生成一分钟k线,一天
func (this Trades) klinesForDay(date time.Time) Klines {
	_930 := 570  //9:30 的分钟
	_1130 := 690 //11:30 的分钟
	_1300 := 780 //13:00 的分钟
	_1500 := 900 //15:00 的分钟
	keys := []int(nil)
	//早上
	m := map[int]Trades{}
	for i := 1; i <= 120; i++ {
		keys = append(keys, _930+i)
		m[_930+i] = []*Trade{}
	}
	//下午
	for i := 1; i <= 120; i++ {
		keys = append(keys, _1300+i)
		m[_1300+i] = []*Trade{}
	}
	//获取开盘价,有可能前几分钟没有数据,先遍历一遍
	var open Price
	for _, v := range this {
		if v.Price > 0 {
			open = v.Price
			break
		}
	}
	//分组,按
	for _, v := range this {
		ms := minutes(v.Time)
		t := conv.Select(ms < _930, _930, ms)
		t++
		t = conv.Select(t > _1130 && t <= _1300, _1130, t)
		t = conv.Select(t > _1500, _1500, t)
		m[t] = append(m[t], v)
	}
	//合并
	ls := []*Kline(nil)
	for _, v := range keys {
		k := m[v].Kline(time.Date(date.Year(), date.Month(), date.Day(), v/60, v%60, 0, 0, date.Location()), open)
		open = k.Close
		ls = append(ls, k)
	}
	return ls
}

type TradeCache struct {
	Date string //日期
	Code string //计算倍数
}
