package config

import (
	"bytes"
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/go-viper/mapstructure/v2"
	"github.com/spf13/viper"

	"github.com/tidbcloud/service-proxy/pkg/audit"
)

// Config is the root configuration structure.
type Config struct {
	Server        ServerConfig  `yaml:"server"`
	Metrics       MetricsConfig `yaml:"metrics"`
	Audit         audit.Config  `yaml:"audit"`
	ClusterRoutes []RouteConfig `yaml:"clusterRoutes"`
	ServiceRoutes []RouteConfig `yaml:"serviceRoutes"`
}

// ServerConfig holds server configuration.
type ServerConfig struct {
	Addr string `yaml:"addr"`
	Port int    `yaml:"port"`
}

// MetricsConfig holds metrics configuration.
type MetricsConfig struct {
	Enabled bool   `yaml:"enabled"`
	Port    int    `yaml:"port"`
	Path    string `yaml:"path"`
}

// RouteConfig defines a proxy route.
type RouteConfig struct {
	Name    string        `yaml:"name"`
	Match   MatchConfig   `yaml:"match"`
	Backend BackendConfig `yaml:"backend"`
	TLS     TLSConfig     `yaml:"tls"`
	Timeout time.Duration `yaml:"timeout"`
}

// MatchConfig defines URL matching rules.
type MatchConfig struct {
	Prefix string `yaml:"prefix"`
}

// BackendConfig defines backend service configuration.
type BackendConfig struct {
	Scheme string `yaml:"scheme"`
	// ServiceNamePattern is a regex pattern to match service names.
	ServiceNamePattern string `yaml:"serviceNamePattern"`
	// ServiceNameTemplate builds service names from path parameters (e.g., "{name}-tibr-headless").
	ServiceNameTemplate string `yaml:"serviceNameTemplate"`
	Port                int    `yaml:"port"`
}

// TLSConfig defines TLS settings for a route.
type TLSConfig struct {
	Enabled            bool            `yaml:"enabled"`
	InsecureSkipVerify bool            `yaml:"insecureSkipVerify"`
	SecretRef          SecretRefConfig `yaml:"secretRef"`
}

// SecretRefConfig references a Kubernetes secret.
type SecretRefConfig struct {
	Name      string `yaml:"name"`
	Namespace string `yaml:"namespace"`
}

// Manager handles configuration loading and hot-reloading.
type Manager struct {
	mu       sync.RWMutex
	config   *Config
	filePath string
}

// NewManager creates a new configuration manager.
func NewManager(filePath string) *Manager {
	return &Manager{
		filePath: filePath,
	}
}

// Load reads and parses configuration from the file.
func (m *Manager) Load() error {
	data, err := os.ReadFile(m.filePath)
	if err != nil {
		return fmt.Errorf("read config file: %w", err)
	}

	return m.loadFromBytes(data)
}

func (m *Manager) loadFromBytes(data []byte) error {
	v := viper.New()
	v.SetConfigType("yaml")
	if err := v.ReadConfig(bytes.NewReader(data)); err != nil {
		return fmt.Errorf("parse config: %w", err)
	}

	var cfg Config
	opt := viper.DecoderConfigOption(func(dc *mapstructure.DecoderConfig) {
		dc.TagName = "yaml"
	})
	if err := v.Unmarshal(&cfg, opt); err != nil {
		return fmt.Errorf("unmarshal config: %w", err)
	}

	if err := Validate(&cfg); err != nil {
		return fmt.Errorf("validate config: %w", err)
	}

	m.mu.Lock()
	m.config = &cfg
	m.mu.Unlock()

	return nil
}

// Get returns the current configuration.
func (m *Manager) Get() *Config {
	m.mu.RLock()
	defer m.mu.RUnlock()
	return m.config
}

// GetFilePath returns the configuration file path.
func (m *Manager) GetFilePath() string {
	return m.filePath
}
