package settings

import (
	"fmt"

	"github.com/qdm12/gosettings"
	"github.com/qdm12/gosettings/reader"
	"github.com/qdm12/gotree"
	"github.com/qdm12/ss-server/pkg/tcpudp"
)

// Shadowsocks contains settings to configure the Shadowsocks server.
type Shadowsocks struct {
	// Enabled is true if the server should be running.
	// It defaults to false, and cannot be nil in the internal state.
	Enabled *bool
	// Settings are settings for the TCP+UDP server.
	Settings tcpudp.Settings
}

func (s Shadowsocks) validate() (err error) {
	return s.Settings.Validate()
}

func (s *Shadowsocks) copy() (copied Shadowsocks) {
	return Shadowsocks{
		Enabled:  gosettings.CopyPointer(s.Enabled),
		Settings: s.Settings.Copy(),
	}
}

// overrideWith overrides fields of the receiver
// settings object with any field set in the other
// settings.
func (s *Shadowsocks) overrideWith(other Shadowsocks) {
	s.Enabled = gosettings.OverrideWithPointer(s.Enabled, other.Enabled)
	s.Settings.OverrideWith(other.Settings)
}

func (s *Shadowsocks) setDefaults() {
	s.Enabled = gosettings.DefaultPointer(s.Enabled, false)
	s.Settings.SetDefaults()
}

func (s Shadowsocks) String() string {
	return s.toLinesNode().String()
}

func (s Shadowsocks) toLinesNode() (node *gotree.Node) {
	node = gotree.New("Shadowsocks server settings:")

	node.Appendf("Enabled: %s", gosettings.BoolToYesNo(s.Enabled))
	if !*s.Enabled {
		return node
	}

	// TODO have ToLinesNode in qdm12/ss-server
	node.Appendf("Listening address: %s", *s.Settings.Address)
	node.Appendf("Cipher: %s", s.Settings.CipherName)
	node.Appendf("Password: %s", gosettings.ObfuscateKey(*s.Settings.Password))
	node.Appendf("Log addresses: %s", gosettings.BoolToYesNo(s.Settings.LogAddresses))

	return node
}

func (s *Shadowsocks) read(r *reader.Reader) (err error) {
	s.Enabled, err = r.BoolPtr("SHADOWSOCKS")
	if err != nil {
		return err
	}

	s.Settings.Address, err = readShadowsocksAddress(r)
	if err != nil {
		return err
	}
	s.Settings.LogAddresses, err = r.BoolPtr("SHADOWSOCKS_LOG")
	if err != nil {
		return err
	}
	s.Settings.CipherName = r.String("SHADOWSOCKS_CIPHER",
		reader.RetroKeys("SHADOWSOCKS_METHOD"))
	s.Settings.Password = r.Get("SHADOWSOCKS_PASSWORD",
		reader.ForceLowercase(false))

	return nil
}

func readShadowsocksAddress(r *reader.Reader) (address *string, err error) {
	const currentKey = "SHADOWSOCKS_LISTENING_ADDRESS"
	port, err := r.Uint16Ptr("SHADOWSOCKS_PORT", reader.IsRetro(currentKey)) // retro-compatibility
	if err != nil {
		return nil, err
	} else if port != nil {
		return ptrTo(fmt.Sprintf(":%d", *port)), nil
	}

	return r.Get(currentKey), nil
}
