package main

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"gopkg.in/yaml.v3"
)

type DebianBlueprint struct {
	Name        string                 `yaml:"name" json:"name"`
	Description string                 `yaml:"description" json:"description"`
	Version     string                 `yaml:"version" json:"version"`
	Variant     string                 `yaml:"variant" json:"variant"`
	Architecture string                `yaml:"architecture" json:"architecture"`
	Packages    BlueprintPackages     `yaml:"packages" json:"packages"`
	Users       []BlueprintUser       `yaml:"users" json:"users"`
	Groups      []BlueprintGroup      `yaml:"groups" json:"groups"`
	Services    []BlueprintService    `yaml:"services" json:"services"`
	Files       []BlueprintFile       `yaml:"files" json:"files"`
	Customizations BlueprintCustomizations `yaml:"customizations" json:"customizations"`
	Created     time.Time             `yaml:"created" json:"created"`
	Modified    time.Time             `yaml:"modified" json:"modified"`
}

type BlueprintPackages struct {
	Include []string `yaml:"include" json:"include"`
	Exclude []string `yaml:"exclude" json:"exclude"`
	Groups  []string `yaml:"groups" json:"groups"`
}

type BlueprintUser struct {
	Name        string   `yaml:"name" json:"name"`
	Description string   `yaml:"description" json:"description"`
	Password    string   `yaml:"password" json:"password"`
	Key         string   `yaml:"key" json:"key"`
	Home        string   `yaml:"home" json:"home"`
	Shell       string   `yaml:"shell" json:"shell"`
	Groups      []string `yaml:"groups" json:"groups"`
	UID         int      `yaml:"uid" json:"uid"`
	GID         int      `yaml:"gid" json:"gid"`
}

type BlueprintGroup struct {
	Name        string `yaml:"name" json:"name"`
	Description string `yaml:"description" json:"description"`
	GID         int    `yaml:"gid" json:"gid"`
}

type BlueprintService struct {
	Name    string `yaml:"name" json:"name"`
	Enabled bool   `yaml:"enabled" json:"enabled"`
	Masked  bool   `yaml:"masked" json:"masked"`
}

type BlueprintFile struct {
	Path        string `yaml:"path" json:"path"`
	User        string `yaml:"user" json:"user"`
	Group       string `yaml:"group" json:"group"`
	Mode        string `yaml:"mode" json:"mode"`
	Data        string `yaml:"data" json:"data"`
	EnsureParents bool `yaml:"ensure_parents" json:"ensure_parents"`
}

type BlueprintCustomizations struct {
	Hostname     string            `yaml:"hostname" json:"hostname"`
	Kernel       BlueprintKernel   `yaml:"kernel" json:"kernel"`
	Timezone     string            `yaml:"timezone" json:"timezone"`
	Locale       string            `yaml:"locale" json:"locale"`
	Firewall     BlueprintFirewall `yaml:"firewall" json:"firewall"`
	SSH          BlueprintSSH      `yaml:"ssh" json:"ssh"`
}

type BlueprintKernel struct {
	Name   string            `yaml:"name" json:"name"`
	Append string            `yaml:"append" json:"append"`
	Remove string            `yaml:"remove" json:"remove"`
}

type BlueprintFirewall struct {
	Services []string `yaml:"services" json:"services"`
	Ports    []string `yaml:"ports" json:"ports"`
}

type BlueprintSSH struct {
	KeyFile string `yaml:"key_file" json:"key_file"`
	User    string `yaml:"user" json:"user"`
}

func loadBlueprint(path string) (*DebianBlueprint, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return nil, fmt.Errorf("cannot read blueprint file: %w", err)
	}

	var blueprint DebianBlueprint
	
	// Try YAML first
	if err := yaml.Unmarshal(data, &blueprint); err != nil {
		// Try JSON if YAML fails
		if err := json.Unmarshal(data, &blueprint); err != nil {
			return nil, fmt.Errorf("cannot parse blueprint file (neither YAML nor JSON): %w", err)
		}
	}

	// Set timestamps if not present
	if blueprint.Created.IsZero() {
		blueprint.Created = time.Now()
	}
	blueprint.Modified = time.Now()

	return &blueprint, nil
}

func saveBlueprint(blueprint *DebianBlueprint, path string, format string) error {
	var data []byte
	var err error

	switch strings.ToLower(format) {
	case "yaml", "yml":
		data, err = yaml.Marshal(blueprint)
	case "json":
		data, err = json.MarshalIndent(blueprint, "", "  ")
	default:
		return fmt.Errorf("unsupported format: %s", format)
	}

	if err != nil {
		return fmt.Errorf("cannot marshal blueprint: %w", err)
	}

	// Ensure directory exists
	dir := filepath.Dir(path)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return fmt.Errorf("cannot create directory: %w", err)
	}

	if err := os.WriteFile(path, data, 0644); err != nil {
		return fmt.Errorf("cannot write blueprint file: %w", err)
	}

	return nil
}

func validateBlueprintStructure(blueprint *DebianBlueprint) error {
	var errors []string

	if blueprint.Name == "" {
		errors = append(errors, "name is required")
	}

	if blueprint.Variant == "" {
		errors = append(errors, "variant is required")
	}

	if blueprint.Architecture == "" {
		errors = append(errors, "architecture is required")
	}

	// Validate variant
	validVariants := []string{"bookworm", "sid", "testing", "backports"}
	validVariant := false
	for _, v := range validVariants {
		if blueprint.Variant == v {
			validVariant = true
			break
		}
	}
	if !validVariant {
		errors = append(errors, fmt.Sprintf("invalid variant: %s (valid: %v)", blueprint.Variant, validVariants))
	}

	// Validate architecture
	validArchs := []string{"amd64", "arm64", "armel", "armhf", "i386", "mips64el", "mipsel", "ppc64el", "s390x"}
	validArch := false
	for _, a := range validArchs {
		if blueprint.Architecture == a {
			validArch = true
			break
		}
	}
	if !validArch {
		errors = append(errors, fmt.Sprintf("invalid architecture: %s (valid: %v)", blueprint.Architecture, validArchs))
	}

	if len(errors) > 0 {
		return fmt.Errorf("blueprint validation failed: %s", strings.Join(errors, "; "))
	}

	return nil
}

func createDefaultBlueprint(name, variant, architecture string) *DebianBlueprint {
	return &DebianBlueprint{
		Name:        name,
		Description: fmt.Sprintf("Debian %s %s blueprint", variant, architecture),
		Version:     "1.0.0",
		Variant:     variant,
		Architecture: architecture,
		Packages: BlueprintPackages{
			Include: []string{"task-minimal"},
			Exclude: []string{},
			Groups:  []string{},
		},
		Users: []BlueprintUser{
			{
				Name:        "debian",
				Description: "Default user",
				Home:        "/home/debian",
				Shell:       "/bin/bash",
				Groups:      []string{"users"},
			},
		},
		Groups: []BlueprintGroup{
			{
				Name:        "users",
				Description: "Default users group",
			},
		},
		Services: []BlueprintService{
			{
				Name:    "ssh",
				Enabled: true,
			},
		},
		Customizations: BlueprintCustomizations{
			Hostname: fmt.Sprintf("%s-%s", name, variant),
			Timezone: "UTC",
			Locale:   "en_US.UTF-8",
			Kernel: BlueprintKernel{
				Name: "linux-image-amd64",
			},
		},
		Created:  time.Now(),
		Modified: time.Now(),
	}
}

func listBlueprints(directory string) ([]string, error) {
	files, err := os.ReadDir(directory)
	if err != nil {
		return nil, fmt.Errorf("cannot read directory: %w", err)
	}

	var blueprints []string
	for _, file := range files {
		if !file.IsDir() {
			ext := strings.ToLower(filepath.Ext(file.Name()))
			if ext == ".yaml" || ext == ".yml" || ext == ".json" {
				blueprints = append(blueprints, file.Name())
			}
		}
	}

	return blueprints, nil
}
