package stack

import (
	"bytes"
	"encoding/json"
	"fmt"
	"gb-cms/common"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

const (
	TransStreamRtmp       = iota + 1
	TransStreamFlv        = 2
	TransStreamRtsp       = 3
	TransStreamHls        = 4
	TransStreamRtc        = 5
	TransStreamGBCascaded = 6 // 国标级联转发
	TransStreamGBTalk     = 7 // 国标广播/对讲转发
	TransStreamGBGateway  = 8 // 国标网关
)

const (
	SourceTypeRtmp = iota + 1
	SourceType28181
	SourceType1078
	SourceTypeGBTalk
)

type SourceDetails struct {
	ID        string    `json:"id"`
	Protocol  string    `json:"protocol"`   // 推流协议
	Time      time.Time `json:"time"`       // 推流时间
	SinkCount int       `json:"sink_count"` // 播放端计数
	Bitrate   string    `json:"bitrate"`    // 码率统计
	Tracks    []string  `json:"tracks"`     // 每路流编码器ID
	Urls      []string  `json:"urls"`       // 拉流地址
}

type SinkDetails struct {
	ID       string    `json:"id"`
	Protocol string    `json:"protocol"` // 拉流协议
	Time     time.Time `json:"time"`     // 拉流时间
	Bitrate  string    `json:"bitrate"`  // 码率统计
	Tracks   []string  `json:"tracks"`   // 每路流编码器ID
}

type SDP struct {
	SessionName string  `json:"session_name,omitempty"` // play/download/playback/talk/broadcast
	Addr        string  `json:"addr,omitempty"`         // 连接地址
	SSRC        string  `json:"ssrc,omitempty"`
	Setup       string  `json:"setup,omitempty"`     // active/passive
	Transport   string  `json:"transport,omitempty"` // tcp/udp
	Speed       float64 `json:"speed"`
	StartTime   int     `json:"start_time,omitempty"`
	EndTime     int     `json:"end_time,omitempty"`
	FileSize    int     `json:"file_size,omitempty"`
}

type SourceSDP struct {
	Source string `json:"source"` // GetSourceID
	SDP
}

type GBOffer struct {
	SourceSDP
	AnswerSetup         string `json:"answer_setup,omitempty"` // 希望应答的连接方式
	TransStreamProtocol int    `json:"trans_stream_protocol,omitempty"`
}

func Send(path string, body interface{}) (*http.Response, error) {
	return SendWithUrlParams(path, body, nil)
}

func SendWithUrlParams(path string, body interface{}, values url.Values) (*http.Response, error) {
	if values != nil {
		params := values.Encode()
		if len(params) > 0 {
			path = fmt.Sprintf("%s?%s", path, params)
		}
	}

	url := fmt.Sprintf("%s/%s", common.Config.MediaServer, path)

	data, err := json.Marshal(body)
	if err != nil {
		return nil, err
	}

	client := &http.Client{
		Timeout: 10 * time.Second,
	}

	request, err := http.NewRequest("post", url, bytes.NewBuffer(data))
	if err != nil {
		return nil, err
	}

	request.Header.Set("Content-Type", "application/json")
	return client.Do(request)
}

func MSCreateGBSource(id, setup string, ssrc string, sessionName string, speed float64) (string, uint16, []string, string, error) {
	v := &SourceSDP{
		Source: id,
		SDP: SDP{
			Setup:       setup,
			SSRC:        ssrc,
			SessionName: sessionName,
			Speed:       speed,
		},
	}

	response, err := Send("api/v1/gb28181/source/create", v)
	if err != nil {
		return "", 0, nil, "", err
	}

	data := &common.Response[struct {
		SDP
		Urls []string `json:"urls"`
	}]{}

	if err = common.DecodeJSONBody(response.Body, data); err != nil {
		return "", 0, nil, "", err
	} else if http.StatusOK != data.Code {
		return "", 0, nil, "", fmt.Errorf(data.Msg)
	}

	host, p, err := net.SplitHostPort(data.Data.Addr)
	if err != nil {
		return "", 0, nil, "", err
	}

	port, err := strconv.Atoi(p)
	return host, uint16(port), data.Data.Urls, data.Data.SSRC, err
}

func MSConnectGBSource(id, addr string, fileSize int) error {
	v := &SourceSDP{
		Source: id,
		SDP: SDP{
			Addr:     addr,
			FileSize: fileSize,
		},
	}

	_, err := Send("api/v1/gb28181/answer/set", v)
	return err
}

func MSCloseSource(id string) error {
	v := &struct {
		Source string `json:"source"`
	}{
		Source: id,
	}

	_, err := Send("api/v1/source/close", v)
	return err
}

func MSCloseSink(sourceId string, sinkId string) {
	v := struct {
		SourceID string `json:"source"`
		SinkID   string `json:"sink"` // sink id
	}{
		sourceId, sinkId,
	}

	_, _ = Send("api/v1/sink/close", v)
}

func MSQuerySourceList() ([]*SourceDetails, error) {
	response, err := Send("api/v1/source/list", nil)
	if err != nil {
		return nil, err
	}

	data := &common.Response[[]*SourceDetails]{}
	if err = common.DecodeJSONBody(response.Body, data); err != nil {
		return nil, err
	}

	return data.Data, err
}

func MSQuerySinkList(source string) ([]*SinkDetails, error) {
	id := struct {
		Source string `json:"source"`
	}{source}

	response, err := Send("api/v1/sink/list", id)
	if err != nil {
		return nil, err
	}

	data := &common.Response[[]*SinkDetails]{}
	if err = common.DecodeJSONBody(response.Body, data); err != nil {
		return nil, err
	}

	return data.Data, err
}

func MSAddForwardSink(protocol int, source, addr, offerSetup, answerSetup, ssrc, sessionName string, values url.Values) (string, uint16, string, string, error) {
	offer := &GBOffer{
		SourceSDP: SourceSDP{
			Source: source,
			SDP: SDP{
				Addr:        addr,
				Setup:       offerSetup,
				SSRC:        ssrc,
				SessionName: sessionName,
			},
		},
		AnswerSetup:         answerSetup,
		TransStreamProtocol: protocol,
	}

	var err error
	response, err := SendWithUrlParams("api/v1/sink/add", offer, values)
	if err != nil {
		return "", 0, "", "", err
	}

	data := &common.Response[struct {
		Sink string `json:"sink"`
		SDP
	}]{}

	if err = common.DecodeJSONBody(response.Body, data); err != nil {
		return "", 0, "", "", err
	} else if http.StatusOK != data.Code {
		return "", 0, "", "", fmt.Errorf(data.Msg)
	}

	host, p, err := net.SplitHostPort(data.Data.Addr)
	if err != nil {
		return "", 0, "", "", err
	}

	port, _ := strconv.Atoi(p)
	return host, uint16(port), data.Data.Sink, data.Data.SSRC, nil
}

func MSQueryStreamInfo(header http.Header, queryParams string) (*http.Response, error) {
	// 构建目标URL
	targetURL := common.Config.MediaServer + "/api/v1/stream/info"
	if queryParams != "" {
		targetURL += "?" + queryParams
	}

	// 创建转发请求
	proxyReq, err := http.NewRequest("POST", targetURL, nil)
	if err != nil {
		return nil, err
	}

	// 复制请求头
	for name, values := range header {
		for _, value := range values {
			proxyReq.Header.Add(name, value)
		}
	}

	// 发送请求
	client := &http.Client{
		Timeout: 30 * time.Second,
	}

	return client.Do(proxyReq)
}

func MSSpeedSet(id string, speed float64) error {
	v := &SourceSDP{
		Source: id,
		SDP: SDP{
			Speed: speed,
		},
	}

	_, err := Send("api/v1/gb28181/speed/set", v)
	return err
}
