package main

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

	builder "deb-bootc-image-builder/internal/builder"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

var (
	verbose bool
	workDir string
)

func main() {
	rootCmd := &cobra.Command{
		Use:   "particle-os",
		Short: "particle-os: Debian-based OS image builder",
		Long: `particle-os is a Debian-native OS image builder that creates bootable images
from container images and recipes, similar to ublue-os but with Debian foundation.

Features:
  • Build from YAML recipes (like BlueBuild)
  • Container-first workflow
  • Multiple output formats (raw, qcow2, vmdk, vdi)
  • OSTree + bootupd integration
  • Debian-native stages and tools`,
		Version: "0.1.0",
	}

	// Global flags
	rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose logging")
	rootCmd.PersistentFlags().StringVarP(&workDir, "work-dir", "w", "", "Working directory for builds")

	// Add commands
	rootCmd.AddCommand(buildCmd())
	rootCmd.AddCommand(listCmd())
	rootCmd.AddCommand(validateCmd())
	rootCmd.AddCommand(versionCmd())
	rootCmd.AddCommand(containerCmd())

	// Execute
	if err := rootCmd.Execute(); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}
}

func buildCmd() *cobra.Command {
	var output string
	var clean bool
	var jsonOutput bool
	var quiet bool

	cmd := &cobra.Command{
		Use:   "build [RECIPE_PATH]",
		Short: "Build OS image from recipe",
		Long: `Build an OS image from a particle-os recipe file.

Examples:
  particle-os build recipes/debian-desktop.yml
  particle-os build --output my-image.img recipes/debian-server.yml
  particle-os build --work-dir /tmp/custom-build recipes/debian-gaming.yml
  particle-os build --json --quiet recipes/debian-server.yml  # CI/CD mode`,
		Args: cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			recipePath := args[0]

			// Setup logging
			if verbose {
				logrus.SetLevel(logrus.DebugLevel)
			} else if quiet {
				logrus.SetLevel(logrus.ErrorLevel) // Only show errors in quiet mode
			} else {
				logrus.SetLevel(logrus.InfoLevel) // Default info level
			}

			// Load recipe
			recipe, err := builder.LoadRecipe(recipePath)
			if err != nil {
				return fmt.Errorf("failed to load recipe: %w", err)
			}

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

			if !quiet {
				fmt.Printf("✅ Recipe loaded: %s\n", recipe.Name)
				fmt.Printf("📦 Base image: %s\n", recipe.BaseImage)
				fmt.Printf("🔧 Stages: %d\n", len(recipe.Stages))
				fmt.Printf("📤 Output formats: %v\n", recipe.Output.Formats)
			}

			// Create builder with appropriate log level
			var logLevel logrus.Level
			if verbose {
				logLevel = logrus.DebugLevel
			} else if quiet {
				logLevel = logrus.ErrorLevel
			} else {
				logLevel = logrus.InfoLevel
			}
			
			imageBuilder := builder.NewBuilder(recipe, workDir, logLevel)

			// Build image
			if !quiet {
				fmt.Println("\n🚀 Starting build...")
			}
			result, err := imageBuilder.Build()
			if err != nil {
				return fmt.Errorf("build failed: %w", err)
			}

			// Handle CI/CD output
			if jsonOutput {
				// Create CI/CD friendly output
				buildResult := map[string]interface{}{
					"success": true,
					"recipe": recipe.Name,
					"base_image": recipe.BaseImage,
					"stages": len(recipe.Stages),
					"output_formats": recipe.Output.Formats,
					"image_path": result.ImagePath,
					"work_directory": result.Metadata["work_dir"],
					"build_time": time.Now().Format(time.RFC3339),
					"exit_code": 0,
				}

				// Add output file info if specified
				if output != "" {
					if err := copyFile(result.ImagePath, output); err != nil {
						buildResult["success"] = false
						buildResult["error"] = err.Error()
						buildResult["exit_code"] = 1
					} else {
						buildResult["output_file"] = output
					}
				}

				// Output JSON
				jsonData, _ := json.MarshalIndent(buildResult, "", "  ")
				fmt.Println(string(jsonData))
			} else {
				// Human-friendly output
				if !quiet {
					fmt.Printf("\n✅ Build completed successfully!\n")
					fmt.Printf("📁 Image created: %s\n", result.ImagePath)
					fmt.Printf("📊 Work directory: %s\n", result.Metadata["work_dir"])
				}

				// Copy to output if specified
				if output != "" {
					if err := copyFile(result.ImagePath, output); err != nil {
						return fmt.Errorf("failed to copy to output: %w", err)
					}
					if !quiet {
						fmt.Printf("📋 Copied to: %s\n", output)
					}
				}
			}

			// Cleanup if requested
			if clean {
				if err := imageBuilder.Cleanup(); err != nil {
					if !quiet {
						fmt.Printf("⚠️  Warning: cleanup failed: %v\n", err)
					}
				} else if !quiet {
					fmt.Println("🧹 Cleanup completed")
				}
			}

			return nil
		},
	}

	cmd.Flags().StringVarP(&output, "output", "o", "", "Output path for the image")
	cmd.Flags().BoolVarP(&clean, "clean", "c", false, "Clean up work directory after build")
	cmd.Flags().BoolVarP(&jsonOutput, "json", "j", false, "Output results in JSON format (CI/CD friendly)")
	cmd.Flags().BoolVarP(&quiet, "quiet", "q", false, "Suppress non-essential output (CI/CD friendly)")

	return cmd
}

func listCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "list",
		Short: "List available recipes",
		Long: `List all available particle-os recipes in the recipes directory.

This command scans the recipes directory and shows all available recipe files
with their basic information.`,
		RunE: func(cmd *cobra.Command, args []string) error {
			recipesDir := "recipes"
			if _, err := os.Stat(recipesDir); os.IsNotExist(err) {
				fmt.Printf("📁 Recipes directory not found: %s\n", recipesDir)
				fmt.Println("💡 Create some recipes first!")
				return nil
			}

			entries, err := os.ReadDir(recipesDir)
			if err != nil {
				return fmt.Errorf("failed to read recipes directory: %w", err)
			}

			fmt.Printf("📚 Available recipes in %s:\n\n", recipesDir)

			recipeCount := 0
			for _, entry := range entries {
				if entry.IsDir() || !isRecipeFile(entry.Name()) {
					continue
				}

				recipePath := filepath.Join(recipesDir, entry.Name())
				recipe, err := builder.LoadRecipe(recipePath)
				if err != nil {
					fmt.Printf("❌ %s: Failed to load (%v)\n", entry.Name(), err)
					continue
				}

				fmt.Printf("📋 %s\n", entry.Name())
				fmt.Printf("   Name: %s\n", recipe.Name)
				fmt.Printf("   Description: %s\n", recipe.Description)
				fmt.Printf("   Base Image: %s\n", recipe.BaseImage)
				fmt.Printf("   Stages: %d\n", len(recipe.Stages))
				fmt.Printf("   Output Formats: %v\n", recipe.Output.Formats)
				fmt.Println()

				recipeCount++
			}

			if recipeCount == 0 {
				fmt.Println("💡 No recipes found. Create some recipe files first!")
			} else {
				fmt.Printf("📊 Total recipes: %d\n", recipeCount)
			}

			return nil
		},
	}

	return cmd
}

func validateCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "validate [RECIPE_PATH]",
		Short: "Validate a particle-os recipe",
		Long: `Validate a particle-os recipe file for syntax and configuration errors.

This command checks the recipe file for:
  • YAML syntax validity
  • Required fields presence
  • Stage configuration correctness
  • Output format specification`,
		Args: cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			recipePath := args[0]

			fmt.Printf("🔍 Validating recipe: %s\n\n", recipePath)

			// Load recipe
			recipe, err := builder.LoadRecipe(recipePath)
			if err != nil {
				return fmt.Errorf("❌ Recipe loading failed: %w", err)
			}

			fmt.Println("✅ Recipe loaded successfully")

			// Validate recipe
			if err := recipe.Validate(); err != nil {
				return fmt.Errorf("❌ Recipe validation failed: %w", err)
			}

			fmt.Println("✅ Recipe validation passed")

			// Show recipe details
			fmt.Printf("\n📋 Recipe Details:\n")
			fmt.Printf("   Name: %s\n", recipe.Name)
			fmt.Printf("   Description: %s\n", recipe.Description)
			fmt.Printf("   Base Image: %s\n", recipe.BaseImage)
			fmt.Printf("   Image Version: %s\n", recipe.ImageVersion)
			fmt.Printf("   Stages: %d\n", len(recipe.Stages))
			fmt.Printf("   Output Formats: %v\n", recipe.Output.Formats)
			fmt.Printf("   Output Size: %s\n", recipe.Output.Size)

			// Show stages
			fmt.Printf("\n🔧 Stages:\n")
			for i, stage := range recipe.Stages {
				fmt.Printf("   %d. %s\n", i+1, stage.Type)
				if len(stage.Options) > 0 {
					fmt.Printf("      Options: %v\n", stage.Options)
				}
			}

			fmt.Printf("\n🎉 Recipe is valid and ready to build!\n")
			return nil
		},
	}

	return cmd
}

func versionCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "version",
		Short: "Show particle-os version",
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("particle-os v0.1.0")
			fmt.Println("Debian-native OS image builder")
			fmt.Println("Built with ❤️  for the Debian community")
		},
	}

	return cmd
}

func containerCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "container [IMAGE_REF]",
		Short: "Show container image information",
		Long: `Show detailed information about a container image.

This command inspects a container image and displays:
  • Image ID and digest
  • Size and creation date
  • Architecture and OS
  • Labels and metadata
  • Layer information`,
		Args: cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			imageRef := args[0]

			fmt.Printf("🔍 Inspecting container: %s\n\n", imageRef)

			// Create container processor
			processor := builder.NewContainerProcessor("/tmp/particle-os-container-info", logrus.InfoLevel)

			// Check availability
			if !processor.IsAvailable() {
				return fmt.Errorf("no container runtime (podman/docker) available")
			}

			fmt.Printf("📦 Container runtime: %s\n", processor.GetContainerRuntime())

			// Get container info
			info, err := processor.GetContainerInfo(imageRef)
			if err != nil {
				return fmt.Errorf("failed to get container info: %w", err)
			}

			// Display info
			fmt.Printf("\n📋 Container Information:\n")
			fmt.Printf("   ID: %s\n", info.ID)
			fmt.Printf("   Digest: %s\n", info.Digest)
			fmt.Printf("   Size: %d bytes (%.2f MB)\n", info.Size, float64(info.Size)/1024/1024)
			fmt.Printf("   Created: %s\n", info.Created)
			fmt.Printf("   OS: %s\n", info.OS)
			fmt.Printf("   Architecture: %s\n", info.Arch)
			fmt.Printf("   Variant: %s\n", info.Variant)

			if len(info.Labels) > 0 {
				fmt.Printf("\n🏷️  Labels:\n")
				for k, v := range info.Labels {
					fmt.Printf("   %s: %s\n", k, v)
				}
			}

			if len(info.Layers) > 0 {
				fmt.Printf("\n📚 Layers: %d\n", len(info.Layers))
			}

			fmt.Printf("\n✅ Container inspection completed\n")
			return nil
		},
	}

	return cmd
}

// Helper functions

func isRecipeFile(filename string) bool {
	ext := filepath.Ext(filename)
	return ext == ".yml" || ext == ".yaml"
}

func copyFile(src, dst string) error {
	sourceFile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sourceFile.Close()

	destFile, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer destFile.Close()

	_, err = destFile.ReadFrom(sourceFile)
	return err
}
