package configmgr

import (
	"fmt"
	"net/netip"

	"github.com/AdguardTeam/golibs/errors"
	"github.com/AdguardTeam/golibs/timeutil"
)

// Configuration Structures

// config is the top-level on-disk configuration structure.
type config struct {
	DNS  *dnsConfig  `yaml:"dns"`
	HTTP *httpConfig `yaml:"http"`
	Log  *logConfig  `yaml:"log"`
	// TODO(a.garipov): Use.
	SchemaVersion int `yaml:"schema_version"`
}

const errNoConf errors.Error = "configuration not found"

// validate returns an error if the configuration structure is invalid.
func (c *config) validate() (err error) {
	if c == nil {
		return errNoConf
	}

	// TODO(a.garipov): Add more validations.

	// Keep this in the same order as the fields in the config.
	validators := []struct {
		validate func() (err error)
		name     string
	}{{
		validate: c.DNS.validate,
		name:     "dns",
	}, {
		validate: c.HTTP.validate,
		name:     "http",
	}, {
		validate: c.Log.validate,
		name:     "log",
	}}

	for _, v := range validators {
		err = v.validate()
		if err != nil {
			return fmt.Errorf("%s: %w", v.name, err)
		}
	}

	return nil
}

// dnsConfig is the on-disk DNS configuration.
type dnsConfig struct {
	Addresses           []netip.AddrPort  `yaml:"addresses"`
	BootstrapDNS        []string          `yaml:"bootstrap_dns"`
	UpstreamDNS         []string          `yaml:"upstream_dns"`
	DNS64Prefixes       []netip.Prefix    `yaml:"dns64_prefixes"`
	UpstreamTimeout     timeutil.Duration `yaml:"upstream_timeout"`
	BootstrapPreferIPv6 bool              `yaml:"bootstrap_prefer_ipv6"`
	UseDNS64            bool              `yaml:"use_dns64"`
}

// validate returns an error if the DNS configuration structure is invalid.
//
// TODO(a.garipov): Add more validations.
func (c *dnsConfig) validate() (err error) {
	// TODO(a.garipov): Add more validations.
	switch {
	case c == nil:
		return errNoConf
	case c.UpstreamTimeout.Duration <= 0:
		return newMustBePositiveError("upstream_timeout", c.UpstreamTimeout)
	default:
		return nil
	}
}

// httpConfig is the on-disk web API configuration.
type httpConfig struct {
	Pprof *httpPprofConfig `yaml:"pprof"`

	// TODO(a.garipov): Document the configuration change.
	Addresses       []netip.AddrPort  `yaml:"addresses"`
	SecureAddresses []netip.AddrPort  `yaml:"secure_addresses"`
	Timeout         timeutil.Duration `yaml:"timeout"`
	ForceHTTPS      bool              `yaml:"force_https"`
}

// validate returns an error if the HTTP configuration structure is invalid.
//
// TODO(a.garipov): Add more validations.
func (c *httpConfig) validate() (err error) {
	switch {
	case c == nil:
		return errNoConf
	case c.Timeout.Duration <= 0:
		return newMustBePositiveError("timeout", c.Timeout)
	default:
		return c.Pprof.validate()
	}
}

// httpPprofConfig is the on-disk pprof configuration.
type httpPprofConfig struct {
	Port    uint16 `yaml:"port"`
	Enabled bool   `yaml:"enabled"`
}

// validate returns an error if the pprof configuration structure is invalid.
func (c *httpPprofConfig) validate() (err error) {
	if c == nil {
		return errNoConf
	}

	return nil
}

// logConfig is the on-disk web API configuration.
type logConfig struct {
	// TODO(a.garipov): Use.
	Verbose bool `yaml:"verbose"`
}

// validate returns an error if the HTTP configuration structure is invalid.
//
// TODO(a.garipov): Add more validations.
func (c *logConfig) validate() (err error) {
	if c == nil {
		return errNoConf
	}

	return nil
}