package particle_os

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

	"gopkg.in/yaml.v3"
)

// Recipe represents a particle-os recipe configuration
type Recipe struct {
	Name         string                 `yaml:"name"`
	Description  string                 `yaml:"description"`
	BaseImage    string                 `yaml:"base-image"`
	ImageVersion string                 `yaml:"image-version"`
	Stages       []Stage               `yaml:"stages"`
	Output       OutputConfig          `yaml:"output"`
	Metadata     map[string]interface{} `yaml:"metadata,omitempty"`
}

// Stage represents a build stage in the recipe
type Stage struct {
	Type    string                 `yaml:"type"`
	Options map[string]interface{} `yaml:"options,omitempty"`
	Inputs  map[string]interface{} `yaml:"inputs,omitempty"`
	Devices map[string]interface{} `yaml:"devices,omitempty"`
	Mounts  []interface{}          `yaml:"mounts,omitempty"`
}

// User represents a user account configuration
type User struct {
	Password string   `yaml:"password,omitempty"`
	Shell    string   `yaml:"shell,omitempty"`
	Groups   []string `yaml:"groups,omitempty"`
	UID      int      `yaml:"uid"`
	GID      int      `yaml:"gid"`
	Home     string   `yaml:"home,omitempty"`
	Comment  string   `yaml:"comment,omitempty"`
}

// UsersOptions represents the options for the users stage
type UsersOptions struct {
	Users        map[string]User `yaml:"users"`
	DefaultShell string          `yaml:"default_shell,omitempty"`
	DefaultHome  string          `yaml:"default_home,omitempty"`
}

// OutputConfig defines the output image configuration
type OutputConfig struct {
	Formats []string `yaml:"formats"`
	Size    string   `yaml:"size"`
	Path    string   `yaml:"path,omitempty"`
}

// LoadRecipe loads a recipe from a YAML file
func LoadRecipe(recipePath string) (*Recipe, error) {
	data, err := os.ReadFile(recipePath)
	if err != nil {
		return nil, fmt.Errorf("failed to read recipe file: %w", err)
	}

	var recipe Recipe
	if err := yaml.Unmarshal(data, &recipe); err != nil {
		return nil, fmt.Errorf("failed to parse recipe YAML: %w", err)
	}

	// Validate required fields
	if recipe.Name == "" {
		return nil, fmt.Errorf("recipe must have a name")
	}
	if recipe.BaseImage == "" {
		return nil, fmt.Errorf("recipe must specify a base-image")
	}
	if len(recipe.Stages) == 0 {
		return nil, fmt.Errorf("recipe must have at least one stage")
	}

	return &recipe, nil
}

// SaveRecipe saves a recipe to a YAML file
func (r *Recipe) SaveRecipe(outputPath string) error {
	data, err := yaml.Marshal(r)
	if err != nil {
		return fmt.Errorf("failed to marshal recipe: %w", err)
	}

	// Ensure output directory exists
	outputDir := filepath.Dir(outputPath)
	if err := os.MkdirAll(outputDir, 0755); err != nil {
		return fmt.Errorf("failed to create output directory: %w", err)
	}

	if err := os.WriteFile(outputPath, data, 0644); err != nil {
		return fmt.Errorf("failed to write recipe file: %w", err)
	}

	return nil
}

// Validate checks if the recipe is valid
func (r *Recipe) Validate() error {
	// Check for required fields
	if r.Name == "" {
		return fmt.Errorf("recipe name is required")
	}
	if r.BaseImage == "" {
		return fmt.Errorf("base-image is required")
	}
	if len(r.Stages) == 0 {
		return fmt.Errorf("at least one stage is required")
	}

	// Validate stages
	for i, stage := range r.Stages {
		if stage.Type == "" {
			return fmt.Errorf("stage %d: type is required", i)
		}
	}

	// Validate output
	if len(r.Output.Formats) == 0 {
		return fmt.Errorf("at least one output format is required")
	}

	return nil
}

// GetStageByType returns all stages of a specific type
func (r *Recipe) GetStageByType(stageType string) []Stage {
	var stages []Stage
	for _, stage := range r.Stages {
		if stage.Type == stageType {
			stages = append(stages, stage)
		}
	}
	return stages
}

// HasStage checks if the recipe has a stage of the specified type
func (r *Recipe) HasStage(stageType string) bool {
	for _, stage := range r.Stages {
		if stage.Type == stageType {
			return true
		}
	}
	return false
}
