package pprof

import (
	"errors"
	"fmt"
	"time"

	"github.com/qdm12/gluetun/internal/httpserver"
	"github.com/qdm12/gosettings"
	"github.com/qdm12/gosettings/reader"
	"github.com/qdm12/gotree"
)

// Settings are the settings for the Pprof service.
type Settings struct {
	// Enabled can be false or true.
	// It defaults to false.
	Enabled *bool
	// See runtime.SetBlockProfileRate
	// Set to 0 to disable profiling.
	BlockProfileRate *int
	// See runtime.SetMutexProfileFraction
	// Set to 0 to disable profiling.
	MutexProfileRate *int
	// HTTPServer contains settings to configure
	// the HTTP server serving pprof data.
	HTTPServer httpserver.Settings
}

func (s *Settings) SetDefaults() {
	s.Enabled = gosettings.DefaultPointer(s.Enabled, false)
	s.HTTPServer.Address = gosettings.DefaultComparable(s.HTTPServer.Address, "localhost:6060")
	const defaultReadTimeout = 5 * time.Minute // for CPU profiling
	s.HTTPServer.ReadTimeout = gosettings.DefaultComparable(s.HTTPServer.ReadTimeout, defaultReadTimeout)
	s.HTTPServer.SetDefaults()
}

func (s Settings) Copy() (copied Settings) {
	return Settings{
		Enabled:          gosettings.CopyPointer(s.Enabled),
		BlockProfileRate: s.BlockProfileRate,
		MutexProfileRate: s.MutexProfileRate,
		HTTPServer:       s.HTTPServer.Copy(),
	}
}

func (s *Settings) OverrideWith(other Settings) {
	s.Enabled = gosettings.OverrideWithPointer(s.Enabled, other.Enabled)
	s.BlockProfileRate = gosettings.OverrideWithPointer(s.BlockProfileRate, other.BlockProfileRate)
	s.MutexProfileRate = gosettings.OverrideWithPointer(s.MutexProfileRate, other.MutexProfileRate)
	s.HTTPServer.OverrideWith(other.HTTPServer)
}

var (
	ErrBlockProfileRateNegative = errors.New("block profile rate cannot be negative")
	ErrMutexProfileRateNegative = errors.New("mutex profile rate cannot be negative")
)

func (s Settings) Validate() (err error) {
	if *s.BlockProfileRate < 0 {
		return fmt.Errorf("%w", ErrBlockProfileRateNegative)
	}

	if *s.MutexProfileRate < 0 {
		return fmt.Errorf("%w", ErrMutexProfileRateNegative)
	}

	return s.HTTPServer.Validate()
}

func (s Settings) ToLinesNode() (node *gotree.Node) {
	if !*s.Enabled {
		return nil
	}

	node = gotree.New("Pprof settings:")

	if *s.BlockProfileRate > 0 {
		node.Appendf("Block profile rate: %d", *s.BlockProfileRate)
	}

	if *s.MutexProfileRate > 0 {
		node.Appendf("Mutex profile rate: %d", *s.MutexProfileRate)
	}

	node.AppendNode(s.HTTPServer.ToLinesNode())

	return node
}

func (s Settings) String() string {
	return s.ToLinesNode().String()
}

func (s *Settings) Read(r *reader.Reader) (err error) {
	s.Enabled, err = r.BoolPtr("PPROF_ENABLED")
	if err != nil {
		return err
	}

	s.BlockProfileRate, err = r.IntPtr("PPROF_BLOCK_PROFILE_RATE")
	if err != nil {
		return err
	}

	s.MutexProfileRate, err = r.IntPtr("PPROF_MUTEX_PROFILE_RATE")
	if err != nil {
		return err
	}

	s.HTTPServer.Address = r.String("PPROF_HTTP_SERVER_ADDRESS")

	return nil
}
