package config

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v3"
)

// RegistryConfig represents the configuration for a container registry
type RegistryConfig struct {
	BaseURL      string `yaml:"base_url"`
	Namespace    string `yaml:"namespace"`
	AuthRequired bool   `yaml:"auth_required"`
	Description  string `yaml:"description"`
}

// ContainerTemplates defines naming templates for containers
type ContainerTemplates struct {
	BootcBase    string `yaml:"bootc_base"`
	BootcBuilder string `yaml:"bootc_builder"`
}

// VersionMappings defines version mappings for different distributions
type VersionMappings struct {
	Debian map[string]string `yaml:"debian"`
}

// DefaultSettings contains default configuration values
type DefaultSettings struct {
	DebianVersion string   `yaml:"debian_version"`
	ImageTypes    []string `yaml:"image_types"`
	OutputDir     string   `yaml:"output_dir"`
	RootfsType    string   `yaml:"rootfs_type"`
	Architecture  string   `yaml:"architecture"`
}

// BuildSettings contains build-specific configuration
type BuildSettings struct {
	ContainerSizeMultiplier int                    `yaml:"container_size_multiplier"`
	MinRootfsSizeGB         int                    `yaml:"min_rootfs_size_gb"`
	DefaultKernelOptions    []string               `yaml:"default_kernel_options"`
	ExperimentalFeatures    map[string]bool        `yaml:"experimental_features"`
}

// RepositoryConfig defines APT repository configuration
type RepositoryConfig struct {
	Debian      map[string]string `yaml:"debian"`
	DebianForge struct {
		BaseURL    string   `yaml:"base_url"`
		Components []string `yaml:"components"`
	} `yaml:"debian_forge"`
	Priorities map[string]int `yaml:"priorities"`
}

// CloudConfig contains cloud upload settings
type CloudConfig struct {
	AWS struct {
		DefaultRegion   string `yaml:"default_region"`
		BucketTemplate  string `yaml:"bucket_template"`
	} `yaml:"aws"`
}

// LoggingConfig defines logging configuration
type LoggingConfig struct {
	Level   string `yaml:"level"`
	Format  string `yaml:"format"`
	Verbose bool   `yaml:"verbose"`
}

// SecurityConfig defines security settings
type SecurityConfig struct {
	RequireHTTPS   bool `yaml:"require_https"`
	VerifyTLS      bool `yaml:"verify_tls"`
	AllowInsecure  bool `yaml:"allow_insecure"`
}

// Config represents the complete configuration structure
type Config struct {
	Registries      map[string]RegistryConfig `yaml:"registries"`
	ActiveRegistry  string                    `yaml:"active_registry"`
	Containers      ContainerTemplates        `yaml:"containers"`
	Versions        VersionMappings           `yaml:"versions"`
	Defaults        DefaultSettings           `yaml:"defaults"`
	Build           BuildSettings             `yaml:"build"`
	Repositories    RepositoryConfig          `yaml:"repositories"`
	Cloud           CloudConfig               `yaml:"cloud"`
	Logging         LoggingConfig             `yaml:"logging"`
	Security        SecurityConfig            `yaml:"security"`
}

// LoadConfig loads configuration from the specified file path
func LoadConfig(configPath string) (*Config, error) {
	if configPath == "" {
		// Try to find config in standard locations
		configPath = findConfigFile()
	}

	if configPath == "" {
		return nil, fmt.Errorf("no configuration file found")
	}

	data, err := os.ReadFile(configPath)
	if err != nil {
		return nil, fmt.Errorf("failed to read config file %s: %w", configPath, err)
	}

	var config Config
	if err := yaml.Unmarshal(data, &config); err != nil {
		return nil, fmt.Errorf("failed to parse config file %s: %w", configPath, err)
	}

	// Validate configuration
	if err := config.Validate(); err != nil {
		return nil, fmt.Errorf("invalid configuration: %w", err)
	}

	return &config, nil
}

// findConfigFile searches for configuration file in standard locations
func findConfigFile() string {
	// Search paths in order of preference
	searchPaths := []string{
		"./.config/registry.yaml",
		"./registry.yaml",
		"~/.config/debian-bootc-image-builder/registry.yaml",
		"/etc/debian-bootc-image-builder/registry.yaml",
	}

	for _, path := range searchPaths {
		if strings.HasPrefix(path, "~/") {
			homeDir, err := os.UserHomeDir()
			if err != nil {
				continue
			}
			path = filepath.Join(homeDir, path[2:])
		}

		if _, err := os.Stat(path); err == nil {
			return path
		}
	}

	return ""
}

// Validate checks if the configuration is valid
func (c *Config) Validate() error {
	// Check if active registry exists
	if c.ActiveRegistry == "" {
		return fmt.Errorf("active_registry is required")
	}

	if _, exists := c.Registries[c.ActiveRegistry]; !exists {
		return fmt.Errorf("active registry '%s' not found in registries", c.ActiveRegistry)
	}

	// Validate registry configurations
	for name, registry := range c.Registries {
		if registry.BaseURL == "" {
			return fmt.Errorf("registry '%s' missing base_url", name)
		}
		if registry.Namespace == "" {
			return fmt.Errorf("registry '%s' missing namespace", name)
		}
	}

	// Validate container templates
	if c.Containers.BootcBase == "" {
		return fmt.Errorf("bootc_base container template is required")
	}
	if c.Containers.BootcBuilder == "" {
		return fmt.Errorf("bootc_builder container template is required")
	}

	// Validate version mappings
	if len(c.Versions.Debian) == 0 {
		return fmt.Errorf("debian version mappings are required")
	}

	return nil
}

// GetActiveRegistry returns the currently active registry configuration
func (c *Config) GetActiveRegistry() (*RegistryConfig, error) {
	registry, exists := c.Registries[c.ActiveRegistry]
	if !exists {
		return nil, fmt.Errorf("active registry '%s' not found", c.ActiveRegistry)
	}
	return &registry, nil
}

// GetContainerName generates a container name using the specified template
func (c *Config) GetContainerName(template string, variables map[string]string) (string, error) {
	registry, err := c.GetActiveRegistry()
	if err != nil {
		return "", err
	}

	// Add default variables
	if variables == nil {
		variables = make(map[string]string)
	}
	variables["registry"] = registry.BaseURL
	variables["namespace"] = registry.Namespace

	// Replace variables in template
	result := template
	for key, value := range variables {
		placeholder := "{" + key + "}"
		result = strings.ReplaceAll(result, placeholder, value)
	}

	// Check for unresolved variables
	if strings.Contains(result, "{") {
		return "", fmt.Errorf("unresolved variables in template: %s", result)
	}

	return result, nil
}

// GetBootcBaseName generates the bootc base container name
func (c *Config) GetBootcBaseName(version string) (string, error) {
	variables := map[string]string{
		"version": version,
	}
	return c.GetContainerName(c.Containers.BootcBase, variables)
}

// GetBootcBuilderName generates the bootc builder container name
func (c *Config) GetBootcBuilderName(tag string) (string, error) {
	variables := map[string]string{
		"tag": tag,
	}
	return c.GetContainerName(c.Containers.BootcBuilder, variables)
}

// GetDebianVersion returns the actual Debian version for a given version name
func (c *Config) GetDebianVersion(versionName string) (string, error) {
	if version, exists := c.Versions.Debian[versionName]; exists {
		return version, nil
	}
	return "", fmt.Errorf("unknown Debian version: %s", versionName)
}

// GetDefaultDebianVersion returns the default Debian version
func (c *Config) GetDefaultDebianVersion() (string, error) {
	return c.GetDebianVersion(c.Defaults.DebianVersion)
}

// IsExperimentalFeatureEnabled checks if an experimental feature is enabled
func (c *Config) IsExperimentalFeatureEnabled(feature string) bool {
	if enabled, exists := c.Build.ExperimentalFeatures[feature]; exists {
		return enabled
	}
	return false
}

// GetRepositoryURL returns the URL for a specific repository
func (c *Config) GetRepositoryURL(repoName string) (string, error) {
	if url, exists := c.Repositories.Debian[repoName]; exists {
		return url, nil
	}
	return "", fmt.Errorf("unknown repository: %s", repoName)
}

// GetRepositoryPriority returns the priority for a specific repository
func (c *Config) GetRepositoryPriority(repoName string) int {
	if priority, exists := c.Repositories.Priorities[repoName]; exists {
		return priority
	}
	return 500 // default priority
}

// GetDebianForgeRepository returns the debian-forge repository configuration
func (c *Config) GetDebianForgeRepository() (string, []string) {
	return c.Repositories.DebianForge.BaseURL, c.Repositories.DebianForge.Components
}

// GetDefaultKernelOptions returns the default kernel options
func (c *Config) GetDefaultKernelOptions() []string {
	return c.Build.DefaultKernelOptions
}

// GetContainerSizeMultiplier returns the container size multiplier
func (c *Config) GetContainerSizeMultiplier() int {
	return c.Build.ContainerSizeMultiplier
}

// GetMinRootfsSizeGB returns the minimum root filesystem size in GB
func (c *Config) GetMinRootfsSizeGB() int {
	return c.Build.MinRootfsSizeGB
}
