package particle_os

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

	"github.com/sirupsen/logrus"
)

// PackageManager handles package operations in the extracted container
type PackageManager struct {
	logger    *logrus.Logger
	rootfsDir string
	workDir   string
}

// NewPackageManager creates a new package manager
func NewPackageManager(rootfsDir, workDir string, logLevel logrus.Level) *PackageManager {
	logger := logrus.New()
	logger.SetLevel(logLevel)
	
	return &PackageManager{
		logger:    logger,
		rootfsDir: rootfsDir,
		workDir:   workDir,
	}
}

// writeFileWithSudo writes a file to the rootfs using sudo to handle permission issues
func (pm *PackageManager) writeFileWithSudo(path string, content []byte, mode os.FileMode) error {
	// Create a temporary file in the work directory
	tempFile := filepath.Join(pm.workDir, "temp_"+filepath.Base(path))
	if err := os.WriteFile(tempFile, content, mode); err != nil {
		return fmt.Errorf("failed to create temp file: %w", err)
	}
	defer os.Remove(tempFile) // Clean up temp file

	// Use sudo to copy the file to the target location
	cmd := exec.Command("sudo", "cp", tempFile, path)
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to copy file with sudo: %w", err)
	}

	// Set the correct permissions
	chmodCmd := exec.Command("sudo", "chmod", fmt.Sprintf("%o", mode), path)
	if err := chmodCmd.Run(); err != nil {
		pm.logger.Warnf("Failed to set permissions on %s: %v", path, err)
	}

	return nil
}

// removeFileWithSudo removes a file from the rootfs using sudo
func (pm *PackageManager) removeFileWithSudo(path string) error {
	cmd := exec.Command("sudo", "rm", "-f", path)
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to remove file with sudo: %w", err)
	}
	return nil
}

// createSymlinkWithSudo creates a symlink in the rootfs using sudo
func (pm *PackageManager) createSymlinkWithSudo(target, linkPath string) error {
	// Remove existing symlink if it exists
	pm.removeFileWithSudo(linkPath)

	// Create symlink with sudo
	cmd := exec.Command("sudo", "ln", "-s", target, linkPath)
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to create symlink with sudo: %w", err)
	}

	return nil
}

// InstallPackages installs packages using apt
func (pm *PackageManager) InstallPackages(packages []string, update, clean bool) error {
	pm.logger.Infof("Installing packages: %v", packages)

	if len(packages) == 0 {
		pm.logger.Info("No packages to install")
		return nil
	}

	// Update package lists if requested
	if update {
		if err := pm.updatePackageLists(); err != nil {
			return fmt.Errorf("failed to update package lists: %w", err)
		}
	}

	// Install packages
	if err := pm.installPackages(packages); err != nil {
		return fmt.Errorf("failed to install packages: %w", err)
	}

	// Clean package cache if requested
	if clean {
		if err := pm.cleanPackageCache(); err != nil {
			return fmt.Errorf("failed to clean package cache: %w", err)
		}
	}

	pm.logger.Info("Package installation completed successfully")
	return nil
}

// updatePackageLists updates the package lists
func (pm *PackageManager) updatePackageLists() error {
	pm.logger.Info("Updating package lists")

	// Create a chroot environment for apt with sudo
	cmd := exec.Command("sudo", "/usr/sbin/chroot", pm.rootfsDir, "apt-get", "update")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("apt-get update failed: %w", err)
	}

	pm.logger.Info("Package lists updated successfully")
	return nil
}

// installPackages installs the specified packages
func (pm *PackageManager) installPackages(packages []string) error {
	pm.logger.Infof("Installing %d packages", len(packages))

	// Prepare apt-get install command
	args := []string{"apt-get", "install", "-y", "--no-install-recommends"}
	args = append(args, packages...)

	// Execute in chroot with sudo
	chrootArgs := append([]string{"/usr/sbin/chroot", pm.rootfsDir}, args...)
	cmd := exec.Command("sudo", chrootArgs...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("apt-get install failed: %w", err)
	}

	pm.logger.Infof("Successfully installed %d packages", len(packages))
	return nil
}

// cleanPackageCache cleans the package cache
func (pm *PackageManager) cleanPackageCache() error {
	pm.logger.Info("Cleaning package cache")

	cmd := exec.Command("sudo", "/usr/sbin/chroot", pm.rootfsDir, "apt-get", "clean")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("apt-get clean failed: %w", err)
	}

	pm.logger.Info("Package cache cleaned successfully")
	return nil
}

// ConfigureSources configures package sources with optional apt-cacher-ng support
func (pm *PackageManager) ConfigureSources(mirror string, components []string, additionalSources []string) error {
	pm.logger.Infof("Configuring package sources: %s", mirror)

	// Create sources.list.d directory
	sourcesDir := filepath.Join(pm.rootfsDir, "etc/apt/sources.list.d")
	if err := os.MkdirAll(sourcesDir, 0755); err != nil {
		return fmt.Errorf("failed to create sources directory: %w", err)
	}

	// Check for apt-cacher-ng configuration (optional)
	aptCacheURL := pm.detectAptCacherNG()
	if aptCacheURL != "" {
		pm.logger.Infof("apt-cacher-ng detected at: %s (optional enhancement)", aptCacheURL)
		mirror = pm.convertToAptCacherNG(mirror, aptCacheURL)
	} else {
		pm.logger.Info("apt-cacher-ng not detected, using direct repository URLs")
	}

	// Create main sources.list
	mainSources := filepath.Join(pm.rootfsDir, "etc/apt/sources.list")
	mainContent := fmt.Sprintf("deb %s trixie %s\n", mirror, strings.Join(components, " "))
	
	if err := os.WriteFile(mainSources, []byte(mainContent), 0644); err != nil {
		return fmt.Errorf("failed to write main sources.list: %w", err)
	}

	// Add additional sources with optional apt-cacher-ng support
	for i, source := range additionalSources {
		// Convert additional sources to use apt-cacher-ng if available
		if aptCacheURL != "" {
			source = pm.convertToAptCacherNG(source, aptCacheURL)
		}
		
		sourceFile := filepath.Join(sourcesDir, fmt.Sprintf("additional-%d.list", i))
		if err := os.WriteFile(sourceFile, []byte(source+"\n"), 0644); err != nil {
			return fmt.Errorf("failed to write additional source %d: %w", i, err)
		}
	}

	pm.logger.Info("Package sources configured successfully")
	return nil
}

// detectAptCacherNG detects if apt-cacher-ng is available and returns the URL (optional)
func (pm *PackageManager) detectAptCacherNG() string {
	// Check environment variables first (CI/CD friendly)
	if envURL := os.Getenv("APT_CACHER_NG_URL"); envURL != "" {
		return envURL
	}
	
	// Check common apt-cacher-ng URLs (optional enhancement)
	commonURLs := []string{
		"http://192.168.1.101:3142",  // Your specific setup
		"http://localhost:3142",       // Local development
		"http://apt-cacher-ng:3142",  // Docker container
		"http://192.168.1.100:3142",  // Common local network
	}
	
	for _, url := range commonURLs {
		if pm.isAptCacherNGAvailable(url) {
			return url
		}
	}
	
	return "" // No apt-cacher-ng found, which is perfectly fine
}

// isAptCacherNGAvailable checks if apt-cacher-ng is responding at the given URL (optional)
func (pm *PackageManager) isAptCacherNGAvailable(url string) bool {
	// Simple HTTP check - we could make this more sophisticated
	cmd := exec.Command("curl", "-s", "--connect-timeout", "5", "--max-time", "10", url)
	if err := cmd.Run(); err != nil {
		return false
	}
	return true
}

// convertToAptCacherNG converts a standard Debian mirror URL to use apt-cacher-ng (optional enhancement)
func (pm *PackageManager) convertToAptCacherNG(originalURL, cacheURL string) string {
	// Handle different URL patterns
	if strings.HasPrefix(originalURL, "https://") {
		// Convert https://deb.debian.org/debian to http://cache:3142/HTTPS///deb.debian.org/debian
		url := strings.TrimPrefix(originalURL, "https://")
		return fmt.Sprintf("%s/HTTPS///%s", cacheURL, url)
	} else if strings.HasPrefix(originalURL, "http://") {
		// Convert http://mirror to http://cache:3142/mirror
		url := strings.TrimPrefix(originalURL, "http://")
		return fmt.Sprintf("%s/%s", cacheURL, url)
	} else if strings.HasPrefix(originalURL, "deb ") {
		// Handle deb lines
		parts := strings.Fields(originalURL)
		if len(parts) >= 2 {
			url := parts[1]
			if strings.HasPrefix(url, "https://") {
				url = strings.TrimPrefix(url, "https://")
				parts[1] = fmt.Sprintf("%s/HTTPS///%s", cacheURL, url)
			} else if strings.HasPrefix(url, "http://") {
				url = strings.TrimPrefix(url, "http://")
				parts[1] = fmt.Sprintf("%s/%s", cacheURL, url)
			}
			return strings.Join(parts, " ")
		}
	}
	
	// Return original if we can't parse it
	return originalURL
}

// InstallDebootstrap installs debootstrap if not present
func (pm *PackageManager) InstallDebootstrap() error {
	pm.logger.Info("Checking debootstrap installation")

	// Check if debootstrap is already installed in the host system
	if _, err := exec.LookPath("debootstrap"); err == nil {
		pm.logger.Info("Debootstrap already available in host system")
		return nil
	}

	// Also check common locations
	commonPaths := []string{"/usr/sbin/debootstrap", "/usr/bin/debootstrap", "/bin/debootstrap"}
	for _, path := range commonPaths {
		if _, err := os.Stat(path); err == nil {
			pm.logger.Infof("Debootstrap found at: %s", path)
			return nil
		}
	}

	pm.logger.Info("Debootstrap not found in host system")
	pm.logger.Info("Please install debootstrap manually: sudo apt-get install debootstrap")
	
	// For now, return an error to inform the user
	return fmt.Errorf("debootstrap not available in host system - please install manually")
}

// CreateDebootstrap installs a base system using debootstrap
func (pm *PackageManager) CreateDebootstrap(suite, target, arch, variant string, components []string) error {
	pm.logger.Infof("Creating debootstrap system: %s/%s (%s)", suite, arch, variant)

	// Ensure debootstrap is installed
	if err := pm.InstallDebootstrap(); err != nil {
		return fmt.Errorf("debootstrap not available: %w", err)
	}

	// Find debootstrap path
	debootstrapPath := "/usr/sbin/debootstrap"
	if _, err := os.Stat(debootstrapPath); err != nil {
		// Try alternative paths
		altPaths := []string{"/usr/bin/debootstrap", "/bin/debootstrap"}
		for _, path := range altPaths {
			if _, err := os.Stat(path); err == nil {
				debootstrapPath = path
				break
			}
		}
	}

	// Prepare debootstrap command
	args := []string{}
	
	if variant != "" {
		args = append(args, "--variant", variant)
	}
	
	if len(components) > 0 {
		args = append(args, "--components", strings.Join(components, ","))
	}
	
	args = append(args, suite, target, "https://deb.debian.org/debian")

	// Execute debootstrap with full path
	cmd := exec.Command(debootstrapPath, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("debootstrap failed: %w", err)
	}

	pm.logger.Info("Debootstrap system created successfully")
	return nil
}

// ConfigureLocale configures system locale
func (pm *PackageManager) ConfigureLocale(language, defaultLocale string, additionalLocales []string) error {
	pm.logger.Infof("Configuring locale: %s (default: %s)", language, defaultLocale)

	// Generate locales
	locales := []string{language}
	locales = append(locales, additionalLocales...)

	// Create locale.gen content
	localeGenContent := ""
	for _, locale := range locales {
		localeGenContent += locale + " UTF-8\n"
	}

	localeGenPath := filepath.Join(pm.rootfsDir, "etc/locale.gen")
	if err := pm.writeFileWithSudo(localeGenPath, []byte(localeGenContent), 0644); err != nil {
		return fmt.Errorf("failed to write locale.gen: %w", err)
	}

	// Generate locales in chroot with sudo
	cmd := exec.Command("sudo", "/usr/sbin/chroot", pm.rootfsDir, "locale-gen")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("locale-gen failed: %w", err)
	}

	// Set default locale
	defaultLocalePath := filepath.Join(pm.rootfsDir, "etc/default/locale")
	defaultContent := fmt.Sprintf("LANG=%s\n", defaultLocale)
	
	if err := pm.writeFileWithSudo(defaultLocalePath, []byte(defaultContent), 0644); err != nil {
		return fmt.Errorf("failed to write default locale: %w", err)
	}

	pm.logger.Info("Locale configuration completed successfully")
	return nil
}

// ConfigureTimezone configures system timezone
func (pm *PackageManager) ConfigureTimezone(timezone string) error {
	pm.logger.Infof("Configuring timezone: %s", timezone)

	// Create timezone file
	timezonePath := filepath.Join(pm.rootfsDir, "etc/timezone")
	if err := pm.writeFileWithSudo(timezonePath, []byte(timezone+"\n"), 0644); err != nil {
		return fmt.Errorf("failed to write timezone: %w", err)
	}

	// Create localtime symlink
	zoneInfoPath := filepath.Join(pm.rootfsDir, "usr/share/zoneinfo", timezone)
	localtimePath := filepath.Join(pm.rootfsDir, "etc/localtime")

	// Remove existing localtime if it exists
	pm.removeFileWithSudo(localtimePath)

	// Create symlink to zoneinfo
	if err := pm.createSymlinkWithSudo(zoneInfoPath, localtimePath); err != nil {
		return fmt.Errorf("failed to create localtime symlink: %w", err)
	}

	pm.logger.Info("Timezone configuration completed successfully")
	return nil
}

// CreateUser creates a user account
func (pm *PackageManager) CreateUser(username, password, shell string, groups []string, uid, gid int, home, comment string) error {
	pm.logger.Infof("Creating user: %s (UID: %d, GID: %d)", username, uid, gid)

	// Create user using chroot with sudo
	useraddArgs := []string{"useradd", "--create-home", "--shell", shell, "--uid", fmt.Sprintf("%d", uid), "--gid", fmt.Sprintf("%d", gid), "--comment", comment, username}
	
	chrootArgs := append([]string{"/usr/sbin/chroot", pm.rootfsDir}, useraddArgs...)
	cmd := exec.Command("sudo", chrootArgs...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("useradd failed: %w", err)
	}

	// Add user to groups
	if len(groups) > 0 {
		usermodArgs := []string{"usermod", "--append", "--groups", strings.Join(groups, ","), username}
		
		chrootArgs := append([]string{"/usr/sbin/chroot", pm.rootfsDir}, usermodArgs...)
		cmd := exec.Command("sudo", chrootArgs...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		if err := cmd.Run(); err != nil {
			return fmt.Errorf("usermod failed: %w", err)
		}
	}

	// Set password if provided
	if password != "" {
		chpasswdCmd := fmt.Sprintf("echo '%s:%s' | chpasswd", username, password)
		
		cmd := exec.Command("sudo", "/usr/sbin/chroot", pm.rootfsDir, "bash", "-c", chpasswdCmd)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		if err := cmd.Run(); err != nil {
			return fmt.Errorf("password setting failed: %w", err)
		}
	}

	pm.logger.Infof("User %s created successfully", username)
	return nil
}

// InstallEssentialPackages installs essential system packages
func (pm *PackageManager) InstallEssentialPackages() error {
	pm.logger.Info("Installing essential system packages")

	essentialPackages := []string{
		"systemd",
		"systemd-sysv",
		"systemd-resolved",
		"dbus",
		"udev",
		"init",
		"bash",
		"coreutils",
		"util-linux",
		"procps",
		"grep",
		"sed",
		"gawk",
		"tar",
		"gzip",
		"bzip2",
		"xz-utils",
		"ca-certificates",
		"wget",
		"curl",
	}

	return pm.InstallPackages(essentialPackages, false, false)
}
