package config

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"os"
)

// TLS mode values for TLS_MODE env var.
const (
	TLSModeNone   = "none"   // Plain HTTP (default). Use behind a proxy.
	TLSModeServer = "server" // Server-side TLS only. No client cert required.
	TLSModeMTLS   = "mtls"   // Mutual TLS. Client cert required and verified.
)

var (
	ErrTLSCertRequired = errors.New("TLS_CERT and TLS_KEY required when TLS_MODE is server or mtls")
	ErrTLSCARequired   = errors.New("TLS_CA required when TLS_MODE is mtls")
	ErrTLSCALoad       = errors.New("failed to load TLS CA certificate")
	ErrTLSModeInvalid  = errors.New("TLS_MODE must be none, server, or mtls")
)

// ValidateTLSMode returns an error if the TLS_MODE value is not recognized.
func ValidateTLSMode(mode string) error {
	switch mode {
	case TLSModeNone, TLSModeServer, TLSModeMTLS, "":
		return nil
	default:
		return fmt.Errorf("%w: %q", ErrTLSModeInvalid, mode)
	}
}

// BuildTLSConfig constructs a *tls.Config from the application config.
// Returns nil when TLS_MODE is "none" (or empty).
// Returns an error if required cert/key/CA files are missing or unreadable.
func BuildTLSConfig(cfg *Config) (*tls.Config, error) {
	mode := cfg.TLSMode
	if mode == "" || mode == TLSModeNone {
		return nil, nil
	}

	if cfg.TLSCert == "" || cfg.TLSKey == "" {
		return nil, ErrTLSCertRequired
	}

	tlsCfg := &tls.Config{
		MinVersion: tls.VersionTLS13,
	}

	if mode == TLSModeMTLS {
		if cfg.TLSCA == "" {
			return nil, ErrTLSCARequired
		}
		caCert, err := os.ReadFile(cfg.TLSCA)
		if err != nil {
			return nil, fmt.Errorf("%w: %v", ErrTLSCALoad, err)
		}
		pool := x509.NewCertPool()
		if !pool.AppendCertsFromPEM(caCert) {
			return nil, fmt.Errorf("%w: no valid certificates in %s", ErrTLSCALoad, cfg.TLSCA)
		}
		tlsCfg.ClientAuth = tls.RequireAndVerifyClientCert
		tlsCfg.ClientCAs = pool
	}

	return tlsCfg, nil
}

// BuildPeerTLSConfig constructs a *tls.Config for outbound node-to-node
// connections. The node presents its own cert and verifies peers against
// the cluster CA. Returns nil when TLS_MODE is not "mtls".
func BuildPeerTLSConfig(cfg *Config) (*tls.Config, error) {
	if cfg.TLSMode != TLSModeMTLS {
		return nil, nil
	}

	cert, err := tls.LoadX509KeyPair(cfg.TLSCert, cfg.TLSKey)
	if err != nil {
		return nil, fmt.Errorf("failed to load node keypair: %w", err)
	}

	caCert, err := os.ReadFile(cfg.TLSCA)
	if err != nil {
		return nil, fmt.Errorf("%w: %v", ErrTLSCALoad, err)
	}
	pool := x509.NewCertPool()
	if !pool.AppendCertsFromPEM(caCert) {
		return nil, fmt.Errorf("%w: no valid certificates in %s", ErrTLSCALoad, cfg.TLSCA)
	}

	return &tls.Config{
		Certificates: []tls.Certificate{cert},
		RootCAs:      pool,
		MinVersion:   tls.VersionTLS13,
	}, nil
}
