// ABOUTME: YAML configuration parser for EdgeConnect apply command with comprehensive validation
// ABOUTME: Handles parsing and validation of EdgeConnectConfig files with detailed error messages
package config

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

	"gopkg.in/yaml.v3"
)

// Parser defines the interface for configuration parsing
type Parser interface {
	ParseFile(filename string) (*EdgeConnectConfig, string, error)
	ParseBytes(data []byte) (*EdgeConnectConfig, error)
	Validate(config *EdgeConnectConfig) error
}

// ConfigParser implements the Parser interface
type ConfigParser struct{}

// NewParser creates a new configuration parser
func NewParser() Parser {
	return &ConfigParser{}
}

// ParseFile parses an EdgeConnectConfig from a YAML file
func (p *ConfigParser) ParseFile(filename string) (*EdgeConnectConfig, string, error) {
	if filename == "" {
		return nil, "", fmt.Errorf("filename cannot be empty")
	}

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

	// Read file contents
	data, err := os.ReadFile(filename)
	if err != nil {
		return nil, "", fmt.Errorf("failed to read configuration file %s: %w", filename, err)
	}

	// Parse YAML without validation first
	config, err := p.parseYAMLOnly(data)
	if err != nil {
		return nil, "", fmt.Errorf("failed to parse configuration file %s: %w", filename, err)
	}

	// Resolve relative paths relative to config file directory
	configDir := filepath.Dir(filename)
	if err := p.resolveRelativePaths(config, configDir); err != nil {
		return nil, "", fmt.Errorf("failed to resolve paths in %s: %w", filename, err)
	}

	// Now validate with resolved paths
	if err := p.Validate(config); err != nil {
		return nil, "", fmt.Errorf("configuration validation failed in %s: %w", filename, err)
	}

	manifest, err := p.readManifestFiles(config)
	if err != nil {
		return nil, "", fmt.Errorf("failed to read manifest files: %w", err)
	}

	return config, manifest, nil
}

// parseYAMLOnly parses YAML without validation
func (p *ConfigParser) parseYAMLOnly(data []byte) (*EdgeConnectConfig, error) {
	if len(data) == 0 {
		return nil, fmt.Errorf("configuration data cannot be empty")
	}

	var config EdgeConnectConfig

	// Parse YAML with strict mode
	decoder := yaml.NewDecoder(nil)
	decoder.KnownFields(true) // Fail on unknown fields

	if err := yaml.Unmarshal(data, &config); err != nil {
		return nil, fmt.Errorf("YAML parsing failed: %w", err)
	}

	return &config, nil
}

// ParseBytes parses an EdgeConnectConfig from YAML bytes
func (p *ConfigParser) ParseBytes(data []byte) (*EdgeConnectConfig, error) {
	// Parse YAML only
	config, err := p.parseYAMLOnly(data)
	if err != nil {
		return nil, err
	}

	// Validate the parsed configuration
	if err := p.Validate(config); err != nil {
		return nil, fmt.Errorf("configuration validation failed: %w", err)
	}

	return config, nil
}

// Validate performs comprehensive validation of the configuration
func (p *ConfigParser) Validate(config *EdgeConnectConfig) error {
	if config == nil {
		return fmt.Errorf("configuration cannot be nil")
	}

	return config.Validate()
}

// resolveRelativePaths converts relative paths to absolute paths based on config directory
func (p *ConfigParser) resolveRelativePaths(config *EdgeConnectConfig, configDir string) error {
	if config.Spec.K8sApp != nil {
		resolved := config.Spec.K8sApp.GetManifestPath(configDir)
		config.Spec.K8sApp.ManifestFile = resolved
	}

	if config.Spec.DockerApp != nil && config.Spec.DockerApp.ManifestFile != "" {
		resolved := config.Spec.DockerApp.GetManifestPath(configDir)
		config.Spec.DockerApp.ManifestFile = resolved
	}

	return nil
}

// ValidateManifestFiles performs additional validation on manifest files
func (p *ConfigParser) readManifestFiles(config *EdgeConnectConfig) (string, error) {
	var manifestFile string

	if config.Spec.K8sApp != nil {
		manifestFile = config.Spec.K8sApp.ManifestFile
	} else if config.Spec.DockerApp != nil {
		manifestFile = config.Spec.DockerApp.ManifestFile
	}

	if manifestFile == "" {
		return "", nil
	}

	if manifestFile != "" {
		if err := p.validateManifestFile(manifestFile); err != nil {
			return "", fmt.Errorf("manifest file validation failed: %w", err)
		}
	}

	// Try to read the file to ensure it's accessible
	content, err := os.ReadFile(manifestFile)
	if err != nil {
		return "", fmt.Errorf("cannot read manifest file %s: %w", manifestFile, err)
	}

	return string(content), nil
}

// validateManifestFile checks if the manifest file is valid and readable
func (p *ConfigParser) validateManifestFile(filename string) error {
	info, err := os.Stat(filename)
	if err != nil {
		return fmt.Errorf("cannot access manifest file %s: %w", filename, err)
	}

	if info.IsDir() {
		return fmt.Errorf("manifest file cannot be a directory: %s", filename)
	}

	if info.Size() == 0 {
		return fmt.Errorf("manifest file cannot be empty: %s", filename)
	}

	return nil
}
