package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"

	"github.com/spf13/cobra"
	"debian-bootc-image-builder/internal/config"
	"debian-bootc-image-builder/internal/ux"
)

func main() {
	// Global flags
	var verbose, debug, runDiagnostics bool

	rootCmd := &cobra.Command{
		Use:   "debian-bootc-image-builder",
		Short: "Create a bootable image from a Debian bootc container",
		Long: `Create a bootable image from a Debian bootc container.

This tool builds bootable disk images from Debian bootc containers using APT
for package management and OSBuild for image generation.

Examples:
  # Build a qcow2 image
  debian-bootc-image-builder build git.raines.xyz/particle-os/debian-bootc:latest

  # Build multiple image types
  debian-bootc-image-builder build --type qcow2 --type ami git.raines.xyz/particle-os/debian-bootc:latest

  # Run system diagnostics
  debian-bootc-image-builder diagnose

  # Show detailed help
  debian-bootc-image-builder build --help`,
	}

	// Add global flags
	rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose output")
	rootCmd.PersistentFlags().BoolVarP(&debug, "debug", "d", false, "Enable debug output (includes verbose)")
	rootCmd.PersistentFlags().BoolVar(&runDiagnostics, "diagnose", false, "Run system diagnostics before operations")

	// Load configuration
	cfg, err := config.LoadConfig("")
	if err != nil {
		if verbose || debug {
			log.Printf("Warning: Could not load configuration: %v", err)
			log.Println("Using default settings...")
		}
	}

	buildCmd := &cobra.Command{
		Use:   "build IMAGE_NAME",
		Short: "Build a bootable image from a Debian bootc container",
		Args:  cobra.ExactArgs(1),
		RunE: func(cmd *cobra.Command, args []string) error {
			// Enable debug mode if requested
			if debug {
				verbose = true
			}

			// Run diagnostics if requested
			if runDiagnostics {
				ux.PrintInfo(os.Stdout, "Running system diagnostics...")
				troubleshooter := ux.NewTroubleshootingGuide(verbose)
				results := troubleshooter.RunDiagnostics()
				troubleshooter.PrintDiagnostics(results)
				
				// Check for critical issues
				criticalIssues := 0
				for _, result := range results {
					if result.Critical && strings.Contains(result.Status, "❌") {
						criticalIssues++
					}
				}
				
				if criticalIssues > 0 {
					return ux.NewUserError(
						ux.ErrorTypeValidation,
						fmt.Sprintf("Found %d critical system issues", criticalIssues),
						"System diagnostics failed",
						"Resolve the critical issues above before proceeding",
						nil,
					)
				}
			}

			// Validate inputs
			validator := ux.NewValidator(verbose)
			
			// Validate image reference
			imgref := args[0]
			if result := validator.ValidateImageReference(imgref); !result.Valid {
				validator.PrintValidationResult(result)
				return ux.ValidationError(result.Message, nil)
			}

			// Get and validate command flags
			imageTypes, _ := cmd.Flags().GetStringArray("type")
			if result := validator.ValidateImageTypes(imageTypes); !result.Valid {
				validator.PrintValidationResult(result)
				return ux.ValidationError(result.Message, nil)
			}

			targetArch, _ := cmd.Flags().GetString("target-arch")
			if result := validator.ValidateArchitecture(targetArch); !result.Valid {
				validator.PrintValidationResult(result)
				return ux.ValidationError(result.Message, nil)
			}

			rootfsType, _ := cmd.Flags().GetString("rootfs")
			if result := validator.ValidateRootfsType(rootfsType); !result.Valid {
				validator.PrintValidationResult(result)
				return ux.ValidationError(result.Message, nil)
			}

			aptcache, _ := cmd.Flags().GetString("aptcache")
			if result := validator.ValidateDirectory(aptcache, "APT cache"); !result.Valid {
				validator.PrintValidationResult(result)
				return ux.ValidationError(result.Message, nil)
			}

			configPath, _ := cmd.Flags().GetString("config")
			if result := validator.ValidateConfigFile(configPath); !result.Valid {
				validator.PrintValidationResult(result)
				return ux.ValidationError(result.Message, nil)
			}

			distroDefPaths, _ := cmd.Flags().GetStringArray("distro-def-path")
			for _, path := range distroDefPaths {
				if result := validator.ValidateDistroDefPath(path); !result.Valid {
					validator.PrintValidationResult(result)
					return ux.ValidationError(result.Message, nil)
				}
			}

			// Create progress reporter
			progress := ux.NewProgressReporter(os.Stdout, verbose)
			progress.AddStep("validation", "Input validation")
			progress.AddStep("config", "Configuration loading")
			progress.AddStep("manifest", "Manifest generation")
			progress.AddStep("output", "Output file creation")

			// Start validation step
			progress.StartStep(0)
			progress.CompleteStep(0)

			// Configuration step
			progress.StartStep(1)
			if verbose {
				ux.PrintInfo(os.Stdout, fmt.Sprintf("Building image from: %s", imgref))
				if cfg != nil {
					ux.PrintInfo(os.Stdout, fmt.Sprintf("Using configuration from: %s", cfg.ActiveRegistry))
					registry, err := cfg.GetActiveRegistry()
					if err == nil {
						ux.PrintInfo(os.Stdout, fmt.Sprintf("Registry: %s/%s", registry.BaseURL, registry.Namespace))
					}
				}
			}
			progress.CompleteStep(1)

			// Manifest generation step
			progress.StartStep(2)
			manifest, _, err := manifestFromCobra(cmd, args, nil)
			if err != nil {
				progress.FailStep(2, err)
				return ux.ManifestError("Cannot generate manifest", err)
			}
			progress.CompleteStep(2)

			// Output step
			progress.StartStep(3)
			outputDir := "./output"
			if err := os.MkdirAll(outputDir, 0755); err != nil {
				progress.FailStep(3, err)
				return ux.FilesystemError("Cannot create output directory", err)
			}

			manifestFile := fmt.Sprintf("%s/manifest.json", outputDir)
			manifestData, err := json.MarshalIndent(manifest, "", "  ")
			if err != nil {
				progress.FailStep(3, err)
				return ux.ManifestError("Cannot marshal manifest", err)
			}

			if err := os.WriteFile(manifestFile, manifestData, 0644); err != nil {
				progress.FailStep(3, err)
				return ux.FilesystemError("Cannot write manifest file", err)
			}
			progress.CompleteStep(3)

			// OSBuild execution step
			progress.AddStep("osbuild", "OSBuild image generation")
			progress.StartStep(4)
			
			if verbose {
				ux.PrintInfo(os.Stdout, "Running OSBuild to generate image...")
			}
			
			// Run OSBuild with the generated manifest
			if err := runOSBuild(manifestFile, outputDir, verbose); err != nil {
				progress.FailStep(4, err)
				return ux.BuildError("OSBuild execution failed", err)
			}
			progress.CompleteStep(4)

			// Print summary
			progress.PrintSummary()
			ux.PrintSuccess(os.Stdout, fmt.Sprintf("Image built successfully in: %s", outputDir))
			
			if verbose {
				ux.PrintInfo(os.Stdout, "Built using OSBuild with APT integration for Debian package management.")
				ux.PrintInfo(os.Stdout, "Check the output directory for generated image files.")
			}

			return nil
		},
	}

	diagnoseCmd := &cobra.Command{
		Use:   "diagnose",
		Short: "Run system diagnostics to check prerequisites",
		Long: `Run comprehensive system diagnostics to check if your system
is ready for building Debian bootc images.

This command checks:
- Required tools (apt-cache, podman, qemu-img, file)
- System resources (disk space, memory)
- File permissions
- Network connectivity
- Container runtime functionality`,
		RunE: func(cmd *cobra.Command, args []string) error {
			// Enable debug mode if requested
			if debug {
				verbose = true
			}

			ux.PrintInfo(os.Stdout, "Running comprehensive system diagnostics...")
			troubleshooter := ux.NewTroubleshootingGuide(verbose)
			results := troubleshooter.RunDiagnostics()
			troubleshooter.PrintDiagnostics(results)

			// Check for critical issues
			criticalIssues := 0
			warnings := 0
			for _, result := range results {
				if result.Critical && strings.Contains(result.Status, "❌") {
					criticalIssues++
				} else if strings.Contains(result.Status, "⚠️") {
					warnings++
				}
			}

			fmt.Println()
			if criticalIssues > 0 {
				ux.PrintError(os.Stdout, fmt.Sprintf("Found %d critical issues that must be resolved", criticalIssues))
				ux.PrintInfo(os.Stdout, "Please resolve the critical issues above before building images.")
				return ux.NewUserError(
					ux.ErrorTypeValidation,
					fmt.Sprintf("System has %d critical issues", criticalIssues),
					"System diagnostics failed",
					"Resolve the critical issues above before proceeding",
					nil,
				)
			} else if warnings > 0 {
				ux.PrintWarning(os.Stdout, fmt.Sprintf("Found %d warnings (non-critical)", warnings))
				ux.PrintInfo(os.Stdout, "Your system is ready, but consider addressing the warnings above.")
			} else {
				ux.PrintSuccess(os.Stdout, "All diagnostics passed - your system is ready for building images!")
			}

			return nil
		},
	}

	versionCmd := &cobra.Command{
		Use:   "version",
		Short: "Show version information",
		RunE: func(cmd *cobra.Command, args []string) error {
			fmt.Println("debian-bootc-image-builder v0.1.0")
			fmt.Println("Debian bootc image builder with APT integration")
			fmt.Println("Built with comprehensive error handling and UX improvements")
			return nil
		},
	}

	// Add flags to build command
	buildCmd.Flags().String("config", "", "Path to configuration file")
	buildCmd.Flags().StringArray("type", []string{"qcow2"}, "Image types to build")
	buildCmd.Flags().String("aptcache", "/aptcache", "APT cache directory")
	buildCmd.Flags().String("target-arch", "amd64", "Target architecture")
	buildCmd.Flags().String("rootfs", "", "Root filesystem type")
	buildCmd.Flags().String("build-container", "", "Use a custom container for the image build")
	buildCmd.Flags().StringArray("distro-def-path", []string{"./data/defs"}, "Path to distribution definition files")
	buildCmd.Flags().Bool("librepo", false, "Use librepo for package downloads")

	rootCmd.AddCommand(buildCmd)
	rootCmd.AddCommand(diagnoseCmd)
	rootCmd.AddCommand(versionCmd)

	if err := rootCmd.Execute(); err != nil {
		// Use our enhanced error formatting
		fmt.Print(ux.FormatError(err))
		os.Exit(1)
	}
}

// runOSBuild executes OSBuild with the given manifest file
func runOSBuild(manifestFile, outputDir string, verbose bool) error {
	// Use the system osbuild command
	cmd := exec.Command("osbuild", 
		"--output-directory", outputDir, 
		manifestFile)
	
	if verbose {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	}
	
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("osbuild execution failed: %w", err)
	}
	
	return nil
}

