// ABOUTME: Configuration types for EdgeConnect apply command YAML parsing
// ABOUTME: Defines structs that match EdgeConnectConfig.yaml schema exactly
package config

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

	"gopkg.in/yaml.v3"
)

// EdgeConnectConfig represents the top-level configuration structure
type EdgeConnectConfig struct {
	Kind     string   `yaml:"kind"`
	Metadata Metadata `yaml:"metadata"`
	Spec     Spec     `yaml:"spec"`
}

// Metadata contains configuration metadata
type Metadata struct {
	Name         string `yaml:"name"`
	AppVersion   string `yaml:"appVersion"`
	Organization string `yaml:"organization"`
}

// Spec defines the application and infrastructure specification
type Spec struct {
	K8sApp             *K8sApp         `yaml:"k8sApp,omitempty"`
	DockerApp          *DockerApp      `yaml:"dockerApp,omitempty"`
	InfraTemplate      []InfraTemplate `yaml:"infraTemplate"`
	Network            *NetworkConfig  `yaml:"network,omitempty"`
	DeploymentStrategy string          `yaml:"deploymentStrategy,omitempty"`
}

// K8sApp defines Kubernetes application configuration
type K8sApp struct {
	ManifestFile string `yaml:"manifestFile"`
}

// DockerApp defines Docker application configuration
type DockerApp struct {
	ManifestFile string `yaml:"manifestFile"`
	Image        string `yaml:"image"`
}

// InfraTemplate defines infrastructure deployment targets
type InfraTemplate struct {
	Region       string `yaml:"region"`
	CloudletOrg  string `yaml:"cloudletOrg"`
	CloudletName string `yaml:"cloudletName"`
	FlavorName   string `yaml:"flavorName"`
}

// NetworkConfig defines network configuration
type NetworkConfig struct {
	OutboundConnections []OutboundConnection `yaml:"outboundConnections"`
}

// OutboundConnection defines an outbound network connection
type OutboundConnection struct {
	Protocol     string `yaml:"protocol"`
	PortRangeMin int    `yaml:"portRangeMin"`
	PortRangeMax int    `yaml:"portRangeMax"`
	RemoteCIDR   string `yaml:"remoteCIDR"`
}

// Validate performs comprehensive validation of the configuration
func (c *EdgeConnectConfig) Validate() error {
	if c.Kind == "" {
		return fmt.Errorf("kind is required")
	}

	if c.Kind != "edgeconnect-deployment" {
		return fmt.Errorf("unsupported kind: %s, expected 'edgeconnect-deployment'", c.Kind)
	}

	if err := c.Metadata.Validate(); err != nil {
		return fmt.Errorf("metadata validation failed: %w", err)
	}

	if err := c.Spec.Validate(); err != nil {
		return fmt.Errorf("spec validation failed: %w", err)
	}

	return nil
}

// getDeploymentType determines the deployment type from config
func (c *EdgeConnectConfig) GetDeploymentType() string {
	if c.Spec.IsK8sApp() {
		return "kubernetes"
	}
	return "docker"
}

// getImagePath gets the image path for the application
func (c *EdgeConnectConfig) GetImagePath() string {
	if c.Spec.IsDockerApp() && c.Spec.DockerApp.Image != "" {
		return c.Spec.DockerApp.Image
	}

	// For kubernetes apps, extract image from manifest
	if c.Spec.IsK8sApp() && c.Spec.K8sApp.ManifestFile != "" {
		if image, err := extractImageFromK8sManifest(c.Spec.K8sApp.ManifestFile); err == nil && image != "" {
			return image
		}
	}

	// Fallback default for kubernetes apps
	return "https://registry-1.docker.io/library/nginx:latest"
}

// extractImageFromK8sManifest extracts the container image from a Kubernetes manifest
func extractImageFromK8sManifest(manifestPath string) (string, error) {
	data, err := os.ReadFile(manifestPath)
	if err != nil {
		return "", fmt.Errorf("failed to read manifest: %w", err)
	}

	// Parse multi-document YAML
	decoder := yaml.NewDecoder(strings.NewReader(string(data)))

	for {
		var doc map[string]interface{}
		if err := decoder.Decode(&doc); err != nil {
			break // End of documents or error
		}

		// Check if this is a Deployment
		kind, ok := doc["kind"].(string)
		if !ok || kind != "Deployment" {
			continue
		}

		// Navigate to spec.template.spec.containers[0].image
		spec, ok := doc["spec"].(map[string]interface{})
		if !ok {
			continue
		}

		template, ok := spec["template"].(map[string]interface{})
		if !ok {
			continue
		}

		templateSpec, ok := template["spec"].(map[string]interface{})
		if !ok {
			continue
		}

		containers, ok := templateSpec["containers"].([]interface{})
		if !ok || len(containers) == 0 {
			continue
		}

		firstContainer, ok := containers[0].(map[string]interface{})
		if !ok {
			continue
		}

		image, ok := firstContainer["image"].(string)
		if ok && image != "" {
			return image, nil
		}
	}

	return "", fmt.Errorf("no image found in Deployment manifest")
}

// Validate validates metadata fields
func (m *Metadata) Validate() error {
	if m.Name == "" {
		return fmt.Errorf("metadata.name is required")
	}

	if strings.TrimSpace(m.Name) != m.Name {
		return fmt.Errorf("metadata.name cannot have leading/trailing whitespace")
	}

	if m.AppVersion == "" {
		return fmt.Errorf("metadata.appVersion is required")
	}

	if strings.TrimSpace(m.AppVersion) != m.AppVersion {
		return fmt.Errorf("metadata.appVersion cannot have leading/trailing whitespace")
	}

	if m.Organization == "" {
		return fmt.Errorf("metadata.organization is required")
	}

	if strings.TrimSpace(m.Organization) != m.Organization {
		return fmt.Errorf("metadata.Organization cannot have leading/trailing whitespace")
	}

	return nil
}

// Validate validates spec configuration
func (s *Spec) Validate() error {
	// Must have either k8sApp or dockerApp, but not both
	if s.K8sApp == nil && s.DockerApp == nil {
		return fmt.Errorf("spec must define either k8sApp or dockerApp")
	}

	if s.K8sApp != nil && s.DockerApp != nil {
		return fmt.Errorf("spec cannot define both k8sApp and dockerApp")
	}

	// Validate app configuration
	if s.K8sApp != nil {
		if err := s.K8sApp.Validate(); err != nil {
			return fmt.Errorf("k8sApp validation failed: %w", err)
		}
	}

	if s.DockerApp != nil {
		if err := s.DockerApp.Validate(); err != nil {
			return fmt.Errorf("dockerApp validation failed: %w", err)
		}
	}

	// Infrastructure template is required
	if len(s.InfraTemplate) == 0 {
		return fmt.Errorf("infraTemplate is required and must contain at least one target")
	}

	// Validate each infrastructure template
	for i, infra := range s.InfraTemplate {
		if err := infra.Validate(); err != nil {
			return fmt.Errorf("infraTemplate[%d] validation failed: %w", i, err)
		}
	}

	// Validate network configuration if present
	if s.Network != nil {
		if err := s.Network.Validate(); err != nil {
			return fmt.Errorf("network validation failed: %w", err)
		}
	}

	// Validate deployment strategy if present
	if s.DeploymentStrategy != "" {
		if err := s.ValidateDeploymentStrategy(); err != nil {
			return fmt.Errorf("deploymentStrategy validation failed: %w", err)
		}
	}

	return nil
}

// Validate validates k8s app configuration
func (k *K8sApp) Validate() error {
	if k.ManifestFile == "" {
		return fmt.Errorf("manifestFile is required")
	}

	// Check if manifest file exists
	if _, err := os.Stat(k.ManifestFile); os.IsNotExist(err) {
		return fmt.Errorf("manifestFile does not exist: %s", k.ManifestFile)
	}

	return nil
}

// Validate validates docker app configuration
func (d *DockerApp) Validate() error {
	if d.Image == "" {
		return fmt.Errorf("image is required")
	}

	// Check if manifest file exists if specified
	if d.ManifestFile != "" {
		if _, err := os.Stat(d.ManifestFile); os.IsNotExist(err) {
			return fmt.Errorf("manifestFile does not exist: %s", d.ManifestFile)
		}
	}

	return nil
}

// Validate validates infrastructure template configuration
func (i *InfraTemplate) Validate() error {
	if i.Region == "" {
		return fmt.Errorf("region is required")
	}

	if i.CloudletOrg == "" {
		return fmt.Errorf("cloudletOrg is required")
	}

	if i.CloudletName == "" {
		return fmt.Errorf("cloudletName is required")
	}

	if i.FlavorName == "" {
		return fmt.Errorf("flavorName is required")
	}

	// Validate no leading/trailing whitespace
	fields := map[string]string{
		"region":       i.Region,
		"cloudletOrg":  i.CloudletOrg,
		"cloudletName": i.CloudletName,
		"flavorName":   i.FlavorName,
	}

	for field, value := range fields {
		if strings.TrimSpace(value) != value {
			return fmt.Errorf("%s cannot have leading/trailing whitespace", field)
		}
	}

	return nil
}

// Validate validates network configuration
func (n *NetworkConfig) Validate() error {
	if len(n.OutboundConnections) == 0 {
		return fmt.Errorf("outboundConnections is required when network is specified")
	}

	for i, conn := range n.OutboundConnections {
		if err := conn.Validate(); err != nil {
			return fmt.Errorf("outboundConnections[%d] validation failed: %w", i, err)
		}
	}

	return nil
}

// Validate validates outbound connection configuration
func (o *OutboundConnection) Validate() error {
	if o.Protocol == "" {
		return fmt.Errorf("protocol is required")
	}

	validProtocols := map[string]bool{
		"tcp":  true,
		"udp":  true,
		"icmp": true,
	}

	if !validProtocols[strings.ToLower(o.Protocol)] {
		return fmt.Errorf("protocol must be one of: tcp, udp, icmp")
	}

	if o.PortRangeMin <= 0 || o.PortRangeMin > 65535 {
		return fmt.Errorf("portRangeMin must be between 1 and 65535")
	}

	if o.PortRangeMax <= 0 || o.PortRangeMax > 65535 {
		return fmt.Errorf("portRangeMax must be between 1 and 65535")
	}

	if o.PortRangeMin > o.PortRangeMax {
		return fmt.Errorf("portRangeMin (%d) cannot be greater than portRangeMax (%d)", o.PortRangeMin, o.PortRangeMax)
	}

	if o.RemoteCIDR == "" {
		return fmt.Errorf("remoteCIDR is required")
	}

	return nil
}

// GetManifestPath returns the absolute path to the manifest file
func (k *K8sApp) GetManifestPath(configDir string) string {
	if filepath.IsAbs(k.ManifestFile) {
		return k.ManifestFile
	}
	return filepath.Join(configDir, k.ManifestFile)
}

// GetManifestPath returns the absolute path to the manifest file
func (d *DockerApp) GetManifestPath(configDir string) string {
	if d.ManifestFile == "" {
		return ""
	}
	if filepath.IsAbs(d.ManifestFile) {
		return d.ManifestFile
	}
	return filepath.Join(configDir, d.ManifestFile)
}

// GetManifestFile returns the manifest file path from the active app type
func (s *Spec) GetManifestFile() string {
	if s.K8sApp != nil {
		return s.K8sApp.ManifestFile
	}
	if s.DockerApp != nil {
		return s.DockerApp.ManifestFile
	}
	return ""
}

// IsK8sApp returns true if this is a Kubernetes application
func (s *Spec) IsK8sApp() bool {
	return s.K8sApp != nil
}

// IsDockerApp returns true if this is a Docker application
func (s *Spec) IsDockerApp() bool {
	return s.DockerApp != nil
}

// ValidateDeploymentStrategy validates the deployment strategy value
func (s *Spec) ValidateDeploymentStrategy() error {
	validStrategies := map[string]bool{
		"recreate":   true,
		"blue-green": true, // Future implementation
		"rolling":    true, // Future implementation
	}

	strategy := strings.ToLower(strings.TrimSpace(s.DeploymentStrategy))
	if !validStrategies[strategy] {
		return fmt.Errorf("deploymentStrategy must be one of: recreate, blue-green, rolling")
	}

	return nil
}

// GetDeploymentStrategy returns the deployment strategy, defaulting to "recreate"
func (s *Spec) GetDeploymentStrategy() string {
	if s.DeploymentStrategy == "" {
		return "recreate"
	}
	return strings.ToLower(strings.TrimSpace(s.DeploymentStrategy))
}
