package afreeca

import (
	"fmt"
	"git.beejay.kim/tool/service/ascii"
	"github.com/mailru/easyjson"
	"github.com/spf13/cast"
	"strings"
)

type BridgeCommand string

const (
	BridgeCommandInitGateway       BridgeCommand = "INIT_GW"
	BridgeCommandInitBroadcast     BridgeCommand = "INIT_BROAD"
	BridgeCommandKeepAlive         BridgeCommand = "KEEPALIVE"
	BridgeCommandLogin             BridgeCommand = "FLASH_LOGIN"
	BridgeCommandCertTicket        BridgeCommand = "CERTTICKETEX"
	BridgeCommandUserCount         BridgeCommand = "GETUSERCNT"
	BridgeCommandUserCountExtended BridgeCommand = "GETUSERCNTEX"
	BridgeCommandChannelCommon     BridgeCommand = "JOINCH_COMMON"
	BridgeCommandChannelInfo       BridgeCommand = "GETCHINFOEX"
	BridgeCommand_GETBJADCON       BridgeCommand = "GETBJADCON"
	BridgeCommand_GETITEM_SELL     BridgeCommand = "GETITEM_SELL"
	BridgeCommandClosed            BridgeCommand = "CLOSECH"
)

//easyjson:json
type BridgeMessage struct {
	Command BridgeCommand  `json:"SVC"`
	Result  int            `json:"RESULT"`
	Data    map[string]any `json:"DATA"`
}

func (bm *BridgeMessage) AddArgument(k string, v any) {
	switch v.(type) {
	case int:
		bm.Data[k] = v.(int)
	case int8:
		bm.Data[k] = v.(int8)
	case int16:
		bm.Data[k] = v.(int16)
	case int32:
		bm.Data[k] = v.(int32)
	case int64:
		bm.Data[k] = v.(int64)
	case uint:
		bm.Data[k] = v.(uint)
	case uint8:
		bm.Data[k] = v.(uint8)
	case uint16:
		bm.Data[k] = v.(uint16)
	case uint32:
		bm.Data[k] = v.(uint32)
	case uint64:
		bm.Data[k] = v.(uint64)
	default:
		bm.Data[k] = cast.ToString(v)
	}
}

func (bm *BridgeMessage) MustMarshall() []byte {
	buf, err := easyjson.Marshal(bm)
	if err != nil {
		return nil
	}

	return buf
}

type BridgeMessageValue []string

func (arg *BridgeMessageValue) AddTuple(k string, v any) {
	var list []string
	if list = *arg; list == nil {
		list = []string{}
	}

	*arg = append(list, fmt.Sprintf(`%s%s`, k, cast.ToString(v)))
}

func (arg BridgeMessageValue) String() string {
	return strings.Join(arg, "")
}

type BridgeMessageValueParams []string

func (arg BridgeMessageValueParams) String() string {
	return strings.Join(arg, "")
}

func (arg *BridgeMessageValueParams) Add(k string, v any) {
	var list []string
	if list = *arg; list == nil {
		list = []string{}
	}

	ack := string([]byte{ascii.Acknowledgement})
	*arg = append(list, ack, "&", ack, k, ack, "=", ack, cast.ToString(v))
}

func NewBridgeInitGatewayMessage(bid string, uuid string, ch Channel, cookieGetter func(string) string) *BridgeMessage {
	m := NewBridgeMessage(BridgeCommandInitGateway)
	m.AddArgument("BJID", bid)
	m.AddArgument("JOINLOG", makeJoinLog(ch, cookieGetter))
	m.AddArgument("QUALITY", "sd")
	m.AddArgument("addinfo", func() BridgeMessageValue {
		info := BridgeMessageValue{}
		info.AddTuple("ad_lang", "ko")
		info.AddTuple("is_auto", 0)
		return info
	}())
	m.AddArgument("broadno", ch.Id)
	m.AddArgument("category", ch.Category)
	m.AddArgument("cc_cli_type", 19)
	m.AddArgument("cli_type", 44)
	m.AddArgument("cookie", "")
	m.AddArgument("fanticket", ch.FanToken)
	m.AddArgument("gate_ip", ch.GatewayIp)
	m.AddArgument("gate_port", ch.GatewayPort)
	m.AddArgument("guid", uuid)
	m.AddArgument("update_info", 0)
	return m
}

func NewBridgeInitBroadcastMessage(uuid string, ch Channel, cookieGetter func(string) string) *BridgeMessage {
	m := NewBridgeMessage(BridgeCommandInitBroadcast)
	m.AddArgument("JOINLOG", makeJoinLog(ch, cookieGetter))
	m.AddArgument("QUALITY", "sd")
	m.AddArgument("cc_cli_type", 19)
	m.AddArgument("cli_type", 44)
	m.AddArgument("center_ip", ch.CenterIp)
	m.AddArgument("center_port", ch.CenterPort)
	m.AddArgument("guid", uuid)
	m.AddArgument("passwd", "")
	m.AddArgument("append_data", "")
	m.AddArgument("gw_ticket", "")
	return m
}

func NewBridgeMessage(cmd BridgeCommand) *BridgeMessage {
	return &BridgeMessage{
		Command: cmd,
		Data:    map[string]any{},
	}
}

func makeJoinLog(ch Channel, cookieGetter func(string) string) BridgeMessageValue {
	joinlog := BridgeMessageValue{}
	joinlog.AddTuple("log", func() BridgeMessageValueParams {
		params := BridgeMessageValueParams{}
		params.Add("uuid", cookieGetter("_au"))
		params.Add("geo_cc", ch.GeoName)
		params.Add("geo_rc", ch.GeoCode)
		params.Add("acpt_lang", ch.AcceptLanguage)
		params.Add("svc_lang", ch.ServiceLanguage)
		params.Add("os", "mac")
		params.Add("is_streamer", false)
		params.Add("is_rejoin", false)
		params.Add("is_streamer", false)
		params.Add("is_auto", false)
		params.Add("is_support_adaptive", false)
		params.Add("uuid_3rd", cookieGetter("_au3rd"))
		params.Add("subscribe", -1)
		return params
	}())

	joinlog.AddTuple("liveualog", func() BridgeMessageValueParams {
		params := BridgeMessageValueParams{}
		params.Add("is_clearmode", false)
		params.Add("lowlatency", 0)
		return params
	}())

	return joinlog
}