package clash

import (
	"encoding/base64"
	"strings"

	"github.com/sagernet/sing-box/option"
	"github.com/sagernet/sing/common/json/badoption"
)

type HTTPOptions struct {
	Method  string               `yaml:"method,omitempty"`
	Path    []string             `yaml:"path,omitempty"`
	Headers badoption.HTTPHeader `yaml:"headers,omitempty"`
}

type HTTP2Options struct {
	Host []string `yaml:"host,omitempty"`
	Path string   `yaml:"path,omitempty"`
}

type GrpcOptions struct {
	GrpcServiceName string `yaml:"grpc-service-name,omitempty"`
}

type WSOptions struct {
	Path                string            `yaml:"path,omitempty"`
	Headers             map[string]string `yaml:"headers,omitempty"`
	MaxEarlyData        int               `yaml:"max-early-data,omitempty"`
	EarlyDataHeaderName string            `yaml:"early-data-header-name,omitempty"`
	V2rayHttpUpgrade    bool              `yaml:"v2ray-http-upgrade,omitempty"`
}

type MuxOptions struct {
	Enabled        bool           `yaml:"enabled,omitempty"`
	Protocol       string         `yaml:"protocol,omitempty"`
	MaxConnections int            `yaml:"max-connections,omitempty"`
	MinStreams     int            `yaml:"min-streams,omitempty"`
	MaxStreams     int            `yaml:"max-streams,omitempty"`
	Padding        bool           `yaml:"padding,omitempty"`
	BrutalOpts     *BrutalOptions `yaml:"brutal-opts,omitempty"`
}

func (s *MuxOptions) Build() *option.OutboundMultiplexOptions {
	if s == nil {
		return nil
	}
	return &option.OutboundMultiplexOptions{
		Enabled:        s.Enabled,
		Protocol:       s.Protocol,
		MaxConnections: s.MaxConnections,
		MinStreams:     s.MinStreams,
		MaxStreams:     s.MaxStreams,
		Padding:        s.Padding,
		Brutal:         s.BrutalOpts.Build(),
	}
}

type BrutalOptions struct {
	Enabled bool   `yaml:"enabled,omitempty"`
	Up      string `yaml:"up,omitempty"`
	Down    string `yaml:"down,omitempty"`
}

func (b *BrutalOptions) Build() *option.BrutalOptions {
	if b == nil {
		return nil
	}
	return &option.BrutalOptions{
		Enabled:  b.Enabled,
		UpMbps:   clashSpeedToIntMbps(b.Up),
		DownMbps: clashSpeedToIntMbps(b.Down),
	}
}

type RealityOptions struct {
	PublicKey string `yaml:"public-key"`
	ShortID   string `yaml:"short-id"`
}

func (r *RealityOptions) Build() *option.OutboundRealityOptions {
	if r == nil {
		return nil
	}
	return &option.OutboundRealityOptions{
		Enabled:   true,
		PublicKey: r.PublicKey,
		ShortID:   r.ShortID,
	}
}

type ECHOptions struct {
	Enable bool   `yaml:"enable,omitempty"`
	Config string `yaml:"config,omitempty"`
}

func (e *ECHOptions) Build() *option.OutboundECHOptions {
	if e == nil {
		return nil
	}
	list, err := base64.StdEncoding.DecodeString(e.Config)
	if err != nil {
		return nil
	}
	return &option.OutboundECHOptions{
		Enabled: e.Enable,
		Config:  trimStringArray(strings.Split(string(list), "\n")),
	}
}

type TLSOptions struct {
	TLS               bool            `yaml:"tls,omitempty"`
	SNI               string          `yaml:"sni,omitempty"`
	SkipCertVerify    bool            `yaml:"skip-cert-verify,omitempty"`
	ALPN              []string        `yaml:"alpn,omitempty"`
	ClientFingerprint string          `yaml:"client-fingerprint,omitempty"`
	CustomCA          string          `yaml:"ca,omitempty"`
	CustomCAString    string          `yaml:"ca-str,omitempty"`
	Certificate       string          `yaml:"certificate,omitempty"`
	PrivateKey        string          `yaml:"private-key,omitempty"`
	ECHOpts           *ECHOptions     `yaml:"ech-opts,omitempty"`
	RealityOpts       *RealityOptions `yaml:"reality-opts,omitempty"`
}

func (t *TLSOptions) Build() *option.OutboundTLSOptions {
	if t == nil {
		return nil
	}
	options := &option.OutboundTLSOptions{
		Enabled:         t.TLS,
		ServerName:      t.SNI,
		Insecure:        t.SkipCertVerify,
		ALPN:            t.ALPN,
		UTLS:            clashClientFingerprint(t.ClientFingerprint),
		Certificate:     trimStringArray(strings.Split(t.CustomCAString, "\n")),
		CertificatePath: t.CustomCA,
		ECH:             t.ECHOpts.Build(),
		Reality:         t.RealityOpts.Build(),
	}
	if strings.HasPrefix(t.Certificate, "-----BEGIN ") {
		options.ClientCertificate = trimStringArray(strings.Split(t.Certificate, "\n"))
	} else {
		options.ClientCertificatePath = t.Certificate
	}
	if strings.HasPrefix(t.PrivateKey, "-----BEGIN ") {
		options.ClientKey = trimStringArray(strings.Split(t.PrivateKey, "\n"))
	} else {
		options.ClientKeyPath = t.PrivateKey
	}
	return options
}

type DialerOptions struct {
	TFO         bool   `yaml:"tfo,omitempty"`
	MPTCP       bool   `yaml:"mptcp,omitempty"`
	Interface   string `yaml:"interface-name,omitempty"`
	RoutingMark int    `yaml:"routing-mark,omitempty"`
	DialerProxy string `yaml:"dialer-proxy,omitempty"`
}

func (b *DialerOptions) Build() option.DialerOptions {
	return option.DialerOptions{
		Detour:        b.DialerProxy,
		BindInterface: b.Interface,
		TCPFastOpen:   b.TFO,
		TCPMultiPath:  b.MPTCP,
		RoutingMark:   option.FwMark(b.RoutingMark),
	}
}

type ServerOptions struct {
	Server string `yaml:"server"`
	Port   int    `yaml:"port"`
}

func (s *ServerOptions) Build() option.ServerOptions {
	return option.ServerOptions{
		Server:     s.Server,
		ServerPort: uint16(s.Port),
	}
}
