package resolver

import (
	"fmt"
	"net"
	"strconv"
	"strings"
	"time"
)

// ServerMode represents the DNS resolution strategy selected by the -server flag.
type ServerMode struct {
	Mode         string // "default", "local", "gateway", "explicit"
	ExplicitAddr string // IP or IP:port when Mode is "explicit"
}

// QueryConfig holds per-invocation DNS query settings.
type QueryConfig struct {
	Timeout time.Duration // Per-query timeout (default 3s)
	Verbose bool          // Emit diagnostic trace to stderr
}

// BootstrapResolvers is the ordered list of public DNS servers used as fallback
// (and as the complete pool in "default" mode when no local resolvers are found).
var BootstrapResolvers = []string{
	"1.1.1.1",
	"8.8.8.8",
	"1.0.0.1",
	"8.8.4.4",
	"9.9.9.9",
	"208.67.222.222",
}

// PrivateTLDs are locally significant top-level domain suffixes that should
// only be resolved by the local DNS server (FR-007).
var PrivateTLDs = map[string]bool{
	"local":    true,
	"internal": true,
	"lan":      true,
	"home":     true,
	"corp":     true,
	"private":  true,
}

// ParseServerFlag parses the value of the -server CLI flag into a ServerMode.
//
// Recognised values (case-insensitive keywords):
//   - ""         → Mode "default"
//   - "local"    → Mode "local"
//   - "gateway"  → Mode "gateway"
//   - IP         → Mode "explicit", ExplicitAddr set to the bare IP
//   - IP:port    → Mode "explicit", ExplicitAddr set to "IP:port" (port 1-65535)
//
// Any other value returns an error.
func ParseServerFlag(value string) (ServerMode, error) {
	switch strings.ToLower(value) {
	case "":
		return ServerMode{Mode: "default"}, nil
	case "local":
		return ServerMode{Mode: "local"}, nil
	case "gateway":
		return ServerMode{Mode: "gateway"}, nil
	}

	// Try to parse as IP:port first.
	host, portStr, err := net.SplitHostPort(value)
	if err == nil {
		// SplitHostPort succeeded — validate the host and port.
		if net.ParseIP(host) == nil {
			return ServerMode{}, fmt.Errorf("invalid server address %q: host is not a valid IP", value)
		}
		port, convErr := strconv.Atoi(portStr)
		if convErr != nil {
			return ServerMode{}, fmt.Errorf("invalid port in server address %q: %w", value, convErr)
		}
		if port < 1 || port > 65535 {
			return ServerMode{}, fmt.Errorf("invalid port in server address %q: port must be between 1 and 65535", value)
		}
		return ServerMode{Mode: "explicit", ExplicitAddr: value}, nil
	}

	// No port — check if it is a bare IP address.
	if ip := net.ParseIP(value); ip != nil {
		return ServerMode{Mode: "explicit", ExplicitAddr: value}, nil
	}

	return ServerMode{}, fmt.Errorf("invalid server value %q: must be empty, \"local\", \"gateway\", a bare IP, or IP:port", value)
}

// ExtractLabelLevels returns all queryable domain levels from hostname, from
// most-specific to least-specific, excluding single-label names.
//
// Examples:
//   - "host.sub.example.com" → ["host.sub.example.com", "sub.example.com", "example.com"]
//   - "www.example.com"      → ["www.example.com", "example.com"]
//   - "example.com"          → ["example.com"]
//   - "localhost"            → nil
//   - "example.com."         → ["example.com"] (trailing dot stripped)
func ExtractLabelLevels(hostname string) []string {
	// Strip trailing dot (FQDN notation).
	hostname = strings.TrimSuffix(hostname, ".")

	parts := strings.Split(hostname, ".")
	if len(parts) < 2 {
		return nil
	}

	// Generate all suffixes with at least 2 labels.
	var levels []string
	for i := 0; i <= len(parts)-2; i++ {
		levels = append(levels, strings.Join(parts[i:], "."))
	}
	return levels
}
