package main

import (
	"fmt"
	"os"

	"debian-bootc-image-builder/internal/apt"
	"debian-bootc-image-builder/internal/container"
	"debian-bootc-image-builder/internal/config"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"gopkg.in/yaml.v3"
)

// GibiByte represents 1 GiB in bytes
const GibiByte = 1024 * 1024 * 1024

// ManifestConfig contains configuration for manifest generation
type ManifestConfig struct {
	Architecture    string
	Config          *config.Config
	ImageTypes      []string
	Imgref          string
	BuildImgref     string
	RootfsMinsize   uint64
	DistroDefPaths  []string
	SourceInfo      interface{} // Placeholder for osinfo.Info
	BuildSourceInfo interface{} // Placeholder for osinfo.Info
	RootFSType      string
	UseLibrepo      bool
}

// makeManifest creates an OSBuild manifest for Debian images
func makeManifest(c *ManifestConfig, solver *apt.Solver, cacheRoot string) (map[string]interface{}, map[string][]apt.RepoConfig, error) {
	logrus.Debugf("Creating manifest for architecture: %s", c.Architecture)
	
	// Create a simple OSBuild manifest structure with only APT stages
	manifest := map[string]interface{}{
		"version": "2",
		"pipelines": []map[string]interface{}{
			{
				"name":   "build",
				"runner": "org.osbuild.linux",
				"stages": []map[string]interface{}{
					{
						"type": "org.osbuild.apt.depsolve",
						"options": map[string]interface{}{
							"packages": []string{"base-files", "systemd", "linux-image-amd64"},
						},
					},
					{
						"type": "org.osbuild.apt",
						"options": map[string]interface{}{
							"packages": []string{"base-files", "systemd", "linux-image-amd64"},
						},
					},
				},
			},
		},
	}

	// Depsolve packages for each package set
	depsolvedSets := make(map[string]apt.DepsolveResult)
	depsolvedRepos := make(map[string][]apt.RepoConfig)
	
	// Load package definitions for the image type
	packageSet, err := c.loadPackageDefinitions()
	if err != nil {
		return nil, nil, fmt.Errorf("cannot load package definitions: %w", err)
	}
	
	res, err := solver.Depsolve(packageSet, 0)
	if err != nil {
		return nil, nil, fmt.Errorf("cannot depsolve packages: %w", err)
	}
	
	depsolvedSets["build"] = *res
	depsolvedRepos["build"] = res.Repos

	// Add resolved packages to manifest
	if pipelines, ok := manifest["pipelines"].([]map[string]interface{}); ok {
		for _, pipeline := range pipelines {
			if pipeline["name"] == "build" {
				if stages, ok := pipeline["stages"].([]map[string]interface{}); ok {
					// Update depsolve stage with resolved packages
					if len(stages) > 0 && stages[0]["type"] == "org.osbuild.apt.depsolve" {
						if options, ok := stages[0]["options"].(map[string]interface{}); ok {
							packages := make([]string, len(res.Packages))
							for i, pkg := range res.Packages {
								packages[i] = pkg.Name
							}
							options["packages"] = packages
							
							// APT depsolve stage doesn't accept repositories option
							// Repository configuration is handled by the APT stage
						}
					}
					// Update apt stage with resolved packages
					if len(stages) > 1 && stages[1]["type"] == "org.osbuild.apt" {
						if options, ok := stages[1]["options"].(map[string]interface{}); ok {
							packages := make([]string, len(res.Packages))
							for i, pkg := range res.Packages {
								packages[i] = pkg.Name
							}
							options["packages"] = packages
						}
					}
				}
				break
			}
		}
	}

	return manifest, depsolvedRepos, nil
}

// manifestFromCobra creates a manifest from command line arguments
func manifestFromCobra(cmd *cobra.Command, args []string, pbar interface{}) (map[string]interface{}, interface{}, error) {
	if len(args) != 1 {
		return nil, nil, fmt.Errorf("expected exactly one argument (container image reference)")
	}
	
	imgref := args[0]
	logrus.Debugf("Processing container image: %s", imgref)

	// Get command line flags
	userConfigFile, _ := cmd.Flags().GetString("config")
	imgTypes, _ := cmd.Flags().GetStringArray("type")
	aptCacheRoot, _ := cmd.Flags().GetString("aptcache")
	targetArch, _ := cmd.Flags().GetString("target-arch")
	rootFs, _ := cmd.Flags().GetString("rootfs")
	buildImgref, _ := cmd.Flags().GetString("build-container")
	distroDefPaths, _ := cmd.Flags().GetStringArray("distro-def-path")
	useLibrepo, _ := cmd.Flags().GetBool("librepo")

	// Load configuration (optional - use defaults if not found)
	cfg, err := config.LoadConfig(userConfigFile)
	if err != nil {
		logrus.Warnf("Could not load configuration: %v, using defaults", err)
		cfg = nil // Use defaults
	}

	// Set default values
	if aptCacheRoot == "" {
		aptCacheRoot = "/aptcache"
	}
	if targetArch == "" {
		targetArch = "amd64"
	}
	if len(imgTypes) == 0 {
		imgTypes = []string{"qcow2"}
	}
	if len(distroDefPaths) == 0 {
		distroDefPaths = []string{"./data/defs"}
	}

	// Create container instance
	container, err := container.New(imgref)
	if err != nil {
		return nil, nil, fmt.Errorf("cannot create container: %w", err)
	}
	defer func() {
		if err := container.Stop(); err != nil {
			logrus.Warnf("error stopping container: %v", err)
		}
	}()

	// Get container size (simplified)
	cntSize := uint64(10 * GibiByte) // Default size

	// Get root filesystem type
	var rootfsType string
	if rootFs != "" {
		rootfsType = rootFs
	} else {
		rootfsType, err = container.DefaultRootfsType()
		if err != nil {
			return nil, nil, fmt.Errorf("cannot get rootfs type for container: %w", err)
		}
		if rootfsType == "" {
			rootfsType = "ext4"
		}
	}

	// Initialize APT in container
	if err := container.InitAPT(); err != nil {
		return nil, nil, fmt.Errorf("cannot initialize APT: %w", err)
	}

	// Create solver
	solver, err := container.NewContainerSolver(aptCacheRoot, targetArch, nil)
	if err != nil {
		return nil, nil, fmt.Errorf("cannot create solver: %w", err)
	}

	// Create manifest configuration
	var rootfsMinsize uint64
	if cfg != nil {
		rootfsMinsize = cntSize * uint64(cfg.GetContainerSizeMultiplier())
	} else {
		// Default multiplier when no config is available
		rootfsMinsize = cntSize * 2 // Default 2x multiplier
	}
	
	manifestConfig := &ManifestConfig{
		Architecture:    targetArch,
		Config:          cfg,
		ImageTypes:      imgTypes,
		Imgref:          imgref,
		BuildImgref:     buildImgref,
		RootfsMinsize:   rootfsMinsize,
		DistroDefPaths:  distroDefPaths,
		SourceInfo:      nil, // Placeholder
		BuildSourceInfo: nil, // Placeholder
		RootFSType:      rootfsType,
		UseLibrepo:      useLibrepo,
	}

	// Generate manifest
	manifest, repos, err := makeManifest(manifestConfig, solver, aptCacheRoot)
	if err != nil {
		return nil, nil, fmt.Errorf("cannot generate manifest: %w", err)
	}

	return manifest, repos, nil
}

// getContainerSize gets the size of a container (simplified implementation)
func getContainerSize(imgref string) (uint64, error) {
	// This is a simplified implementation
	// In a real implementation, this would:
	// 1. Inspect the container image
	// 2. Calculate the actual size
	// 3. Return the size in bytes
	
	logrus.Debugf("Getting container size for: %s", imgref)
	return 10 * GibiByte, nil // Default 10GB
}

// getDistroAndRunner returns the distribution and runner for Debian
func getDistroAndRunner(osRelease interface{}) (string, interface{}, error) {
	// This is a simplified implementation for Debian
	// In a real implementation, this would parse os-release information
	
	return "debian", &DebianRunner{}, nil
}

// DebianRunner represents a Debian-specific runner
type DebianRunner struct {
	Version string
}

// String returns the string representation of the runner
func (r *DebianRunner) String() string {
	return "debian"
}

// PackageDefinition represents a package definition structure
type PackageDefinition struct {
	Packages []string `yaml:"packages"`
}

// loadPackageDefinitions loads package definitions for the specified image types
func (c *ManifestConfig) loadPackageDefinitions() ([]string, error) {
	// For now, use the first image type to determine packages
	if len(c.ImageTypes) == 0 {
		return []string{"base-files", "systemd", "linux-image-amd64"}, nil
	}
	
	imageType := c.ImageTypes[0]
	
	// Try to load from package definition files
	for _, defPath := range c.DistroDefPaths {
		defFile := fmt.Sprintf("%s/debian-trixie.yaml", defPath)
		if packages, err := c.loadPackagesFromFile(defFile, imageType); err == nil {
			return packages, nil
		}
	}
	
	// Fallback to default packages
	logrus.Warnf("Could not load package definitions for %s, using defaults", imageType)
	return []string{"base-files", "systemd", "linux-image-amd64"}, nil
}

// loadPackagesFromFile loads packages from a YAML definition file
func (c *ManifestConfig) loadPackagesFromFile(filename, imageType string) ([]string, error) {
	data, err := os.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("cannot read package definition file %s: %w", filename, err)
	}
	
	var defs map[string]PackageDefinition
	if err := yaml.Unmarshal(data, &defs); err != nil {
		return nil, fmt.Errorf("cannot parse package definition file %s: %w", filename, err)
	}
	
	if pkgDef, exists := defs[imageType]; exists {
		return pkgDef.Packages, nil
	}
	
	return nil, fmt.Errorf("no package definition found for image type %s", imageType)
}