package config

import (
	"database/sql/driver"
	"fmt"
	"net/url"
	"time"

	"github.com/mcuadros/go-defaults"
	"gopkg.in/yaml.v3"
	"m7s.live/v5/pkg/util"
)

const (
	RelayModeRemux = "remux"
	RelayModeRelay = "relay"
	RelayModeMix   = "mix"

	HookOnPublish         HookType = "publish"
	HookOnSubscribe       HookType = "subscribe"
	HookOnPublishEnd      HookType = "publish_end"
	HookOnSubscribeEnd    HookType = "subscribe_end"
	HookOnServerKeepAlive HookType = "server_keep_alive"
)

type (
	HookType string
	Publish  struct {
		MaxCount          int             `default:"0" desc:"最大发布者数量"` // 最大发布者数量
		PubAudio          bool            `default:"true" desc:"是否发布音频"`
		PubVideo          bool            `default:"true" desc:"是否发布视频"`
		KickExist         bool            `desc:"是否踢掉已经存在的发布者"`                                             // 是否踢掉已经存在的发布者
		PublishTimeout    time.Duration   `default:"10s" desc:"发布无数据超时"`                                    // 发布无数据超时
		WaitCloseTimeout  time.Duration   `desc:"延迟自动关闭（等待重连）"`                                             // 延迟自动关闭（等待重连）
		DelayCloseTimeout time.Duration   `desc:"延迟自动关闭（无订阅时）"`                                             // 延迟自动关闭（无订阅时）
		IdleTimeout       time.Duration   `desc:"空闲(无订阅)超时"`                                                // 空闲(无订阅)超时
		PauseTimeout      time.Duration   `default:"30s" desc:"暂停超时时间"`                                     // 暂停超时
		BufferTime        time.Duration   `desc:"缓冲时长，0代表取最近关键帧"`                                           // 缓冲长度(单位：秒)，0代表取最近关键帧
		Speed             float64         `default:"0" desc:"发送速率"`                                         // 发送速率，0 为不限速
		Scale             float64         `default:"1" desc:"缩放倍数"`                                         // 缩放倍数
		MaxFPS            int             `default:"30" desc:"最大FPS"`                                       // 最大FPS
		Key               string          `desc:"发布鉴权key"`                                                  // 发布鉴权key
		RingSize          util.Range[int] `default:"20-1024" desc:"RingSize范围"`                             // 缓冲区大小范围
		RelayMode         string          `default:"remux" desc:"转发模式" enum:"remux:转格式,relay:纯转发,mix:混合转发"` // 转发模式
		PubType           string          `default:"server" desc:"发布类型"`                                    // 发布类型
		Dump              bool
	}
	Subscribe struct {
		MaxCount        int           `default:"0" desc:"最大订阅者数量"` // 最大订阅者数量
		SubAudio        bool          `default:"true" desc:"是否订阅音频"`
		SubVideo        bool          `default:"true" desc:"是否订阅视频"`
		BufferTime      time.Duration `desc:"缓冲时长,从缓冲时长的关键帧开始播放"`
		SubMode         int           `desc:"订阅模式" enum:"0:实时模式,1:首屏后不进行追赶"`                // 0，实时模式：追赶发布者进度，在播放首屏后等待发布者的下一个关键帧，然后跳到该帧。1、首屏后不进行追赶。2、从缓冲最大的关键帧开始播放，也不追赶，需要发布者配置缓存长度
		SyncMode        int           `default:"1" desc:"同步模式" enum:"0:采用时间戳同步,1:采用写入时间同步"` // 0，采用时间戳同步，1，采用写入时间同步
		IFrameOnly      bool          `desc:"只要关键帧"`                                        // 只要关键帧
		WaitTimeout     time.Duration `default:"10s" desc:"等待流超时时间"`                        // 等待流超时
		WriteBufferSize int           `desc:"写缓冲大小"`                                        // 写缓冲大小
		Key             string        `desc:"订阅鉴权key"`                                      // 订阅鉴权key
		SubType         string        `desc:"订阅类型"`                                         // 订阅类型
	}
	HTTPValues map[string][]string
	Pull       struct {
		URL           string        `desc:"拉流地址"`
		MaxRetry      int           `default:"-1" desc:"断开后自动重试次数,0:不重试,-1:无限重试"` // 断开后自动重拉,0 表示不自动重拉，-1 表示无限重拉，高于0 的数代表最大重拉次数
		RetryInterval time.Duration `default:"5s" desc:"重试间隔"`                    // 重试间隔
		Proxy         string        `desc:"代理地址"`                                 // 代理地址
		Header        HTTPValues
		Args          HTTPValues `gorm:"-:all"` // 拉流参数
	}
	Push struct {
		URL           string        `desc:"推送地址"`                    // 推送地址
		MaxRetry      int           `desc:"断开后自动重试次数,0:不重试,-1:无限重试"` // 断开后自动重推,0 表示不自动重推，-1 表示无限重推，高于0 的数代表最大重推次数
		RetryInterval time.Duration `default:"5s" desc:"重试间隔"`       // 重试间隔
		Proxy         string        `desc:"代理地址"`                    // 代理地址
		Header        HTTPValues
	}
	Record struct {
		Type     string        `desc:"录制类型"`   // 录制类型 mp4、flv、hls、hlsv7
		FilePath string        `desc:"录制文件路径"` // 录制文件路径
		Fragment time.Duration `desc:"分片时长"`   // 分片时长
		Append   bool          `desc:"是否追加录制"` // 是否追加录制
	}
	TransfromOutput struct {
		Target     string `desc:"转码目标"` // 转码目标
		StreamPath string
		Conf       any
	}
	Transform struct {
		Input  any
		Output []TransfromOutput
	}
	OnPublish struct {
		Push      map[Regexp]Push
		Record    map[Regexp]Record
		Transform map[Regexp]Transform
	}
	OnSubscribe struct {
		Pull      map[Regexp]Pull
		Transform map[Regexp]Transform
	}
	Webhook struct {
		URL            string            `yaml:"url" json:"url"`                                  // Webhook 地址
		Method         string            `yaml:"method" json:"method" default:"POST"`             // HTTP 方法
		Headers        map[string]string `yaml:"headers" json:"headers"`                          // 自定义请求头
		TimeoutSeconds int               `yaml:"timeout" json:"timeout" default:"5"`              // 超时时间(秒)
		RetryTimes     int               `yaml:"retry" json:"retry" default:"3"`                  // 重试次数
		RetryInterval  time.Duration     `yaml:"retryInterval" json:"retryInterval" default:"1s"` // 重试间隔
		Interval       int               `yaml:"interval" json:"interval" default:"60"`           // 保活间隔(秒)
	}
	Common struct {
		PublicIP   string
		PublicIPv6 string
		LogLevel   string `default:"info" enum:"trace:跟踪,debug:调试,info:信息,warn:警告,error:错误"` //日志级别
		EnableAuth bool   `desc:"启用鉴权"`                                                      //启用鉴权
		Publish
		Subscribe
		HTTP
		Quic
		TCP
		UDP
		Hook      map[HookType]Webhook
		Pull      map[string]Pull
		Transform map[string]Transform
		OnSub     OnSubscribe
		OnPub     OnPublish
		DB
	}
	ICommonConf interface {
		GetCommonConf() *Common
	}
)

func NewPublish() *Publish {
	p := &Publish{}
	defaults.SetDefaults(p)
	p.RingSize = util.Range[int]{20, 1024}
	return p
}

func (p *Record) GetRecordConfig() *Record {
	return p
}

func (v *HTTPValues) Scan(value any) error {
	bytes, ok := value.([]byte)
	if !ok {
		return fmt.Errorf("failed to unmarshal yaml value: %v", value)
	}
	return yaml.Unmarshal(bytes, v)
}

func (v HTTPValues) Value() (driver.Value, error) {
	return yaml.Marshal(v)
}

func (v HTTPValues) Get(key string) string {
	return url.Values(v).Get(key)
}

func (v HTTPValues) DeepClone() (ret HTTPValues) {
	ret = make(HTTPValues)
	for k, v := range v {
		ret[k] = append([]string(nil), v...)
	}
	return
}
