package distros

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

	"github.com/AvengeMedia/DankMaterialShell/core/internal/deps"
)

func init() {
	Register("opensuse-tumbleweed", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution {
		return NewOpenSUSEDistribution(config, logChan)
	})
	Register("opensuse-leap", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution {
		return NewOpenSUSEDistribution(config, logChan)
	})
	Register("opensuse-slowroll", "#73BA25", FamilySUSE, func(config DistroConfig, logChan chan<- string) Distribution {
		return NewOpenSUSEDistribution(config, logChan)
	})
}

type OpenSUSEDistribution struct {
	*BaseDistribution
	*ManualPackageInstaller
	config DistroConfig
}

const openSUSENiriWaylandServerPackage = "libwayland-server0"

func NewOpenSUSEDistribution(config DistroConfig, logChan chan<- string) *OpenSUSEDistribution {
	base := NewBaseDistribution(logChan)
	return &OpenSUSEDistribution{
		BaseDistribution:       base,
		ManualPackageInstaller: &ManualPackageInstaller{BaseDistribution: base},
		config:                 config,
	}
}

func (o *OpenSUSEDistribution) GetID() string {
	return o.config.ID
}

func (o *OpenSUSEDistribution) GetColorHex() string {
	return o.config.ColorHex
}

func (o *OpenSUSEDistribution) GetFamily() DistroFamily {
	return o.config.Family
}

func (o *OpenSUSEDistribution) GetPackageManager() PackageManagerType {
	return PackageManagerZypper
}

func (o *OpenSUSEDistribution) DetectDependencies(ctx context.Context, wm deps.WindowManager) ([]deps.Dependency, error) {
	return o.DetectDependenciesWithTerminal(ctx, wm, deps.TerminalGhostty)
}

func (o *OpenSUSEDistribution) DetectDependenciesWithTerminal(ctx context.Context, wm deps.WindowManager, terminal deps.Terminal) ([]deps.Dependency, error) {
	var dependencies []deps.Dependency

	// DMS at the top (shell is prominent)
	dependencies = append(dependencies, o.detectDMS())

	// Terminal with choice support
	dependencies = append(dependencies, o.detectSpecificTerminal(terminal))

	// Common detections using base methods
	dependencies = append(dependencies, o.detectGit())
	dependencies = append(dependencies, o.detectWindowManager(wm))
	dependencies = append(dependencies, o.detectQuickshell())
	dependencies = append(dependencies, o.detectDMSGreeter())
	dependencies = append(dependencies, o.detectXDGPortal())
	dependencies = append(dependencies, o.detectAccountsService())

	// Hyprland-specific tools
	if wm == deps.WindowManagerHyprland {
		dependencies = append(dependencies, o.detectHyprlandTools()...)
	}

	// Niri-specific tools
	if wm == deps.WindowManagerNiri {
		dependencies = append(dependencies, o.detectXwaylandSatellite())
	}

	dependencies = append(dependencies, o.detectMatugen())
	dependencies = append(dependencies, o.detectDgop())

	return dependencies, nil
}

func (o *OpenSUSEDistribution) detectXDGPortal() deps.Dependency {
	return o.detectPackage("xdg-desktop-portal-gtk", "Desktop integration portal for GTK", o.packageInstalled("xdg-desktop-portal-gtk"))
}

func (o *OpenSUSEDistribution) packageInstalled(pkg string) bool {
	cmd := exec.Command("rpm", "-q", pkg)
	err := cmd.Run()
	return err == nil
}

func (o *OpenSUSEDistribution) detectDMSGreeter() deps.Dependency {
	return o.detectOptionalPackage("dms-greeter", "DankMaterialShell greetd greeter", o.packageInstalled("dms-greeter"))
}

func (o *OpenSUSEDistribution) GetPackageMapping(wm deps.WindowManager) map[string]PackageMapping {
	return o.GetPackageMappingWithVariants(wm, make(map[string]deps.PackageVariant))
}

func (o *OpenSUSEDistribution) GetPackageMappingWithVariants(wm deps.WindowManager, variants map[string]deps.PackageVariant) map[string]PackageMapping {
	packages := map[string]PackageMapping{
		// Standard zypper packages
		"git":                    {Name: "git", Repository: RepoTypeSystem},
		"kitty":                  {Name: "kitty", Repository: RepoTypeSystem},
		"alacritty":              {Name: "alacritty", Repository: RepoTypeSystem},
		"xdg-desktop-portal-gtk": {Name: "xdg-desktop-portal-gtk", Repository: RepoTypeSystem},
		"accountsservice":        {Name: "accountsservice", Repository: RepoTypeSystem},

		// DMS packages from OBS
		"dms (DankMaterialShell)": o.getDmsMapping(variants["dms (DankMaterialShell)"]),
		"quickshell":              o.getQuickshellMapping(variants["quickshell"]),
		"dms-greeter":             {Name: "dms-greeter", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"},
		"ghostty":                 {Name: "ghostty", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"},
		"matugen":                 {Name: "matugen", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"},
		"dgop":                    {Name: "dgop", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"},
	}

	switch wm {
	case deps.WindowManagerHyprland:
		packages["hyprland"] = PackageMapping{Name: "hyprland", Repository: RepoTypeSystem}
		packages["hyprctl"] = PackageMapping{Name: "hyprland", Repository: RepoTypeSystem}
		packages["jq"] = PackageMapping{Name: "jq", Repository: RepoTypeSystem}
	case deps.WindowManagerNiri:
		// Niri stable has native package support on openSUSE
		niriVariant := variants["niri"]
		packages["niri"] = o.getNiriMapping(niriVariant)
		packages["xwayland-satellite"] = o.getXwaylandSatelliteMapping(niriVariant)
	}

	return packages
}

func (o *OpenSUSEDistribution) getDmsMapping(variant deps.PackageVariant) PackageMapping {
	if variant == deps.VariantGit {
		return PackageMapping{Name: "dms-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:dms-git"}
	}
	return PackageMapping{Name: "dms", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:dms"}
}

func (o *OpenSUSEDistribution) getQuickshellMapping(variant deps.PackageVariant) PackageMapping {
	if forceQuickshellGit || variant == deps.VariantGit {
		return PackageMapping{Name: "quickshell-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}
	}
	return PackageMapping{Name: "quickshell", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}
}

func (o *OpenSUSEDistribution) getNiriMapping(variant deps.PackageVariant) PackageMapping {
	if variant == deps.VariantGit {
		return PackageMapping{Name: "niri-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}
	}
	return PackageMapping{Name: "niri", Repository: RepoTypeSystem}
}

func (o *OpenSUSEDistribution) getXwaylandSatelliteMapping(variant deps.PackageVariant) PackageMapping {
	if variant == deps.VariantGit {
		return PackageMapping{Name: "xwayland-satellite-git", Repository: RepoTypeOBS, RepoURL: "home:AvengeMedia:danklinux"}
	}
	return PackageMapping{Name: "xwayland-satellite", Repository: RepoTypeSystem}
}

func (o *OpenSUSEDistribution) detectXwaylandSatellite() deps.Dependency {
	status := deps.StatusMissing
	if o.commandExists("xwayland-satellite") {
		status = deps.StatusInstalled
	}

	return deps.Dependency{
		Name:        "xwayland-satellite",
		Status:      status,
		Description: "Xwayland support",
		Required:    true,
	}
}

func (o *OpenSUSEDistribution) detectAccountsService() deps.Dependency {
	status := deps.StatusMissing
	if o.packageInstalled("accountsservice") {
		status = deps.StatusInstalled
	}

	return deps.Dependency{
		Name:        "accountsservice",
		Status:      status,
		Description: "D-Bus interface for user account query and manipulation",
		Required:    true,
	}
}

func (o *OpenSUSEDistribution) getPrerequisites() []string {
	return []string{}
}

func (o *OpenSUSEDistribution) InstallPrerequisites(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
	prerequisites := o.getPrerequisites()
	var missingPkgs []string

	progressChan <- InstallProgressMsg{
		Phase:      PhasePrerequisites,
		Progress:   0.06,
		Step:       "Checking prerequisites...",
		IsComplete: false,
		LogOutput:  "Checking prerequisite packages",
	}

	for _, pkg := range prerequisites {
		checkCmd := exec.CommandContext(ctx, "rpm", "-q", pkg)
		if err := checkCmd.Run(); err != nil {
			missingPkgs = append(missingPkgs, pkg)
		}
	}

	_, err := exec.LookPath("go")
	if err != nil {
		o.log("go not found in PATH, will install go")
		missingPkgs = append(missingPkgs, "go")
	} else {
		o.log("go already available in PATH")
	}

	if len(missingPkgs) == 0 {
		o.log("All prerequisites already installed")
		return nil
	}

	o.log(fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", ")))
	progressChan <- InstallProgressMsg{
		Phase:       PhasePrerequisites,
		Progress:    0.08,
		Step:        fmt.Sprintf("Installing %d prerequisites...", len(missingPkgs)),
		IsComplete:  false,
		NeedsSudo:   true,
		CommandInfo: fmt.Sprintf("sudo zypper install -y %s", strings.Join(missingPkgs, " ")),
		LogOutput:   fmt.Sprintf("Installing prerequisites: %s", strings.Join(missingPkgs, ", ")),
	}

	args := []string{"zypper", "install", "-y"}
	args = append(args, missingPkgs...)
	cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " "))
	output, err := cmd.CombinedOutput()
	if err != nil {
		o.logError("failed to install prerequisites", err)
		o.log(fmt.Sprintf("Prerequisites command output: %s", string(output)))
		return fmt.Errorf("failed to install prerequisites: %w", err)
	}
	o.log(fmt.Sprintf("Prerequisites install output: %s", string(output)))

	return nil
}

func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies []deps.Dependency, wm deps.WindowManager, sudoPassword string, reinstallFlags map[string]bool, disabledFlags map[string]bool, skipGlobalUseFlags bool, progressChan chan<- InstallProgressMsg) error {
	// Phase 1: Check Prerequisites
	progressChan <- InstallProgressMsg{
		Phase:      PhasePrerequisites,
		Progress:   0.05,
		Step:       "Checking system prerequisites...",
		IsComplete: false,
		LogOutput:  "Starting prerequisite check...",
	}

	if err := o.disableInstallMediaRepos(ctx, sudoPassword, progressChan); err != nil {
		return fmt.Errorf("failed to disable install media repositories: %w", err)
	}

	if err := o.InstallPrerequisites(ctx, sudoPassword, progressChan); err != nil {
		return fmt.Errorf("failed to install prerequisites: %w", err)
	}

	systemPkgs, obsPkgs, manualPkgs, variantMap := o.categorizePackages(dependencies, wm, reinstallFlags, disabledFlags)

	// Enable OBS repositories
	if len(obsPkgs) > 0 {
		progressChan <- InstallProgressMsg{
			Phase:      PhaseSystemPackages,
			Progress:   0.15,
			Step:       "Enabling OBS repositories...",
			IsComplete: false,
			LogOutput:  "Setting up OBS repositories for additional packages",
		}
		if err := o.enableOBSRepos(ctx, obsPkgs, sudoPassword, progressChan); err != nil {
			return fmt.Errorf("failed to enable OBS repositories: %w", err)
		}
	}

	// Phase 3: System Packages (Zypper)
	if len(systemPkgs) > 0 {
		progressChan <- InstallProgressMsg{
			Phase:      PhaseSystemPackages,
			Progress:   0.35,
			Step:       fmt.Sprintf("Installing %d system packages...", len(systemPkgs)),
			IsComplete: false,
			NeedsSudo:  true,
			LogOutput:  fmt.Sprintf("Installing system packages: %s", strings.Join(systemPkgs, ", ")),
		}
		if err := o.installZypperPackages(ctx, systemPkgs, sudoPassword, progressChan, PhaseSystemPackages, "Installing system packages...", 0.40, 0.60); err != nil {
			return fmt.Errorf("failed to install zypper packages: %w", err)
		}
	}

	// OBS Packages
	obsPkgNames := o.extractPackageNames(obsPkgs)
	if len(obsPkgNames) > 0 {
		progressChan <- InstallProgressMsg{
			Phase:      PhaseAURPackages,
			Progress:   0.65,
			Step:       fmt.Sprintf("Installing %d OBS packages...", len(obsPkgNames)),
			IsComplete: false,
			LogOutput:  fmt.Sprintf("Installing OBS packages: %s", strings.Join(obsPkgNames, ", ")),
		}
		if err := o.installZypperPackages(ctx, obsPkgNames, sudoPassword, progressChan, PhaseAURPackages, "Installing OBS packages...", 0.70, 0.85); err != nil {
			return fmt.Errorf("failed to install OBS packages: %w", err)
		}
	}

	// Manual Builds
	if len(manualPkgs) > 0 {
		progressChan <- InstallProgressMsg{
			Phase:      PhaseSystemPackages,
			Progress:   0.85,
			Step:       fmt.Sprintf("Building %d packages from source...", len(manualPkgs)),
			IsComplete: false,
			LogOutput:  fmt.Sprintf("Building from source: %s", strings.Join(manualPkgs, ", ")),
		}
		if err := o.InstallManualPackages(ctx, manualPkgs, variantMap, sudoPassword, progressChan); err != nil {
			return fmt.Errorf("failed to install manual packages: %w", err)
		}
	}

	// Configuration
	progressChan <- InstallProgressMsg{
		Phase:      PhaseConfiguration,
		Progress:   0.90,
		Step:       "Configuring system...",
		IsComplete: false,
		LogOutput:  "Starting post-installation configuration...",
	}

	terminal := o.DetectTerminalFromDeps(dependencies)
	if err := o.WriteEnvironmentConfig(terminal); err != nil {
		o.log(fmt.Sprintf("Warning: failed to write environment config: %v", err))
	}

	if err := o.WriteWindowManagerConfig(wm); err != nil {
		o.log(fmt.Sprintf("Warning: failed to write window manager config: %v", err))
	}

	if err := o.EnableDMSService(ctx, wm); err != nil {
		o.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err))
	}

	// Complete
	progressChan <- InstallProgressMsg{
		Phase:      PhaseComplete,
		Progress:   1.0,
		Step:       "Installation complete!",
		IsComplete: true,
		LogOutput:  "All packages installed and configured successfully",
	}

	return nil
}

func (o *OpenSUSEDistribution) categorizePackages(dependencies []deps.Dependency, wm deps.WindowManager, reinstallFlags map[string]bool, disabledFlags map[string]bool) ([]string, []PackageMapping, []string, map[string]deps.PackageVariant) {
	systemPkgs := []string{}
	obsPkgs := []PackageMapping{}
	manualPkgs := []string{}

	variantMap := make(map[string]deps.PackageVariant)
	for _, dep := range dependencies {
		variantMap[dep.Name] = dep.Variant
	}

	packageMap := o.GetPackageMappingWithVariants(wm, variantMap)

	for _, dep := range dependencies {
		if disabledFlags[dep.Name] {
			continue
		}

		if dep.Status == deps.StatusInstalled && !reinstallFlags[dep.Name] {
			continue
		}

		pkgInfo, exists := packageMap[dep.Name]
		if !exists {
			o.log(fmt.Sprintf("Warning: No package mapping for %s", dep.Name))
			continue
		}

		switch pkgInfo.Repository {
		case RepoTypeSystem:
			systemPkgs = append(systemPkgs, pkgInfo.Name)
		case RepoTypeOBS:
			obsPkgs = append(obsPkgs, pkgInfo)
		case RepoTypeManual:
			manualPkgs = append(manualPkgs, dep.Name)
		}
	}

	systemPkgs = o.appendMissingSystemPackages(systemPkgs, openSUSENiriRuntimePackages(wm, disabledFlags))

	return systemPkgs, obsPkgs, manualPkgs, variantMap
}

func openSUSENiriRuntimePackages(wm deps.WindowManager, disabledFlags map[string]bool) []string {
	if wm != deps.WindowManagerNiri || disabledFlags["niri"] {
		return nil
	}

	return []string{openSUSENiriWaylandServerPackage}
}

func (o *OpenSUSEDistribution) appendMissingSystemPackages(systemPkgs []string, extraPkgs []string) []string {
	for _, pkg := range extraPkgs {
		if slices.Contains(systemPkgs, pkg) || o.packageInstalled(pkg) {
			continue
		}

		o.log(fmt.Sprintf("Adding openSUSE runtime package: %s", pkg))
		systemPkgs = append(systemPkgs, pkg)
	}

	return systemPkgs
}

func (o *OpenSUSEDistribution) extractPackageNames(packages []PackageMapping) []string {
	names := make([]string, len(packages))
	for i, pkg := range packages {
		names[i] = pkg.Name
	}
	return names
}

func (o *OpenSUSEDistribution) enableOBSRepos(ctx context.Context, obsPkgs []PackageMapping, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
	enabledRepos := make(map[string]bool)

	osInfo, err := GetOSInfo()
	if err != nil {
		return fmt.Errorf("failed to get OS info: %w", err)
	}

	obsDistroVersion := "openSUSE_Tumbleweed"
	switch osInfo.Distribution.ID {
	case "opensuse-leap":
		obsDistroVersion = fmt.Sprintf("openSUSE_Leap_%s", osInfo.VersionID)
	case "opensuse-slowroll":
		obsDistroVersion = "openSUSE_Slowroll"
	}

	for _, pkg := range obsPkgs {
		if pkg.RepoURL != "" && !enabledRepos[pkg.RepoURL] {
			o.log(fmt.Sprintf("Enabling OBS repository: %s", pkg.RepoURL))

			// RepoURL format: "home:AvengeMedia:danklinux"
			repoPath := strings.ReplaceAll(pkg.RepoURL, ":", ":/")
			repoName := strings.ReplaceAll(pkg.RepoURL, ":", "-")
			repoURL := fmt.Sprintf("https://download.opensuse.org/repositories/%s/%s/%s.repo",
				repoPath, obsDistroVersion, pkg.RepoURL)

			checkCmd := exec.CommandContext(ctx, "zypper", "repos", repoName)
			if checkCmd.Run() == nil {
				o.log(fmt.Sprintf("OBS repo %s already exists, skipping", pkg.RepoURL))
				enabledRepos[pkg.RepoURL] = true
				continue
			}

			progressChan <- InstallProgressMsg{
				Phase:       PhaseSystemPackages,
				Progress:    0.20,
				Step:        fmt.Sprintf("Enabling OBS repo %s...", pkg.RepoURL),
				NeedsSudo:   true,
				CommandInfo: fmt.Sprintf("sudo zypper addrepo %s", repoURL),
			}

			cmd := ExecSudoCommand(ctx, sudoPassword,
				fmt.Sprintf("zypper addrepo -f %s", repoURL))
			if err := o.runWithProgress(cmd, progressChan, PhaseSystemPackages, 0.20, 0.22); err != nil {
				o.log(fmt.Sprintf("OBS repo %s add failed (may already exist): %v", pkg.RepoURL, err))
			}

			enabledRepos[pkg.RepoURL] = true
			o.log(fmt.Sprintf("OBS repo %s enabled successfully", pkg.RepoURL))
		}
	}

	// Refresh repositories with GPG auto-import
	if len(enabledRepos) > 0 {
		progressChan <- InstallProgressMsg{
			Phase:       PhaseSystemPackages,
			Progress:    0.25,
			Step:        "Refreshing repositories...",
			NeedsSudo:   true,
			CommandInfo: "sudo zypper --gpg-auto-import-keys refresh",
		}

		refreshCmd := ExecSudoCommand(ctx, sudoPassword, "zypper --gpg-auto-import-keys refresh")
		if err := o.runWithProgress(refreshCmd, progressChan, PhaseSystemPackages, 0.25, 0.27); err != nil {
			return fmt.Errorf("failed to refresh repositories: %w", err)
		}
	}

	return nil
}

func isOpenSUSEInstallMediaURI(uri string) bool {
	normalizedURI := strings.ToLower(strings.TrimSpace(uri))

	return strings.HasPrefix(normalizedURI, "cd:/") ||
		strings.HasPrefix(normalizedURI, "dvd:/") ||
		strings.HasPrefix(normalizedURI, "hd:/") ||
		strings.HasPrefix(normalizedURI, "iso:/")
}

func parseZypperInstallMediaAliases(output string) []string {
	var aliases []string

	for _, line := range strings.Split(output, "\n") {
		line = strings.TrimSpace(line)
		if line == "" || !strings.Contains(line, "|") {
			continue
		}

		parts := strings.Split(line, "|")
		if len(parts) < 7 {
			continue
		}

		for i := range parts {
			parts[i] = strings.TrimSpace(parts[i])
		}

		alias := parts[1]
		enabled := strings.ToLower(parts[3])
		uri := parts[len(parts)-1]

		if alias == "" || strings.EqualFold(alias, "alias") {
			continue
		}
		if enabled != "" && enabled != "yes" {
			continue
		}
		if !isOpenSUSEInstallMediaURI(uri) {
			continue
		}

		aliases = append(aliases, alias)
	}

	return aliases
}

func (o *OpenSUSEDistribution) disableInstallMediaRepos(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
	listCmd := exec.CommandContext(ctx, "zypper", "repos", "-u")
	output, err := listCmd.CombinedOutput()
	if err != nil {
		o.log(fmt.Sprintf("Warning: failed to list zypper repositories: %s", strings.TrimSpace(string(output))))
		return fmt.Errorf("failed to list zypper repositories: %w", err)
	}

	aliases := parseZypperInstallMediaAliases(string(output))
	if len(aliases) == 0 {
		return nil
	}

	o.log(fmt.Sprintf("Disabling install media repositories: %s", strings.Join(aliases, ", ")))
	progressChan <- InstallProgressMsg{
		Phase:       PhasePrerequisites,
		Progress:    0.055,
		Step:        "Disabling install media repositories...",
		IsComplete:  false,
		NeedsSudo:   true,
		CommandInfo: fmt.Sprintf("sudo zypper modifyrepo -d %s", strings.Join(aliases, " ")),
		LogOutput:   fmt.Sprintf("Disabling install media repositories: %s", strings.Join(aliases, ", ")),
	}

	for _, alias := range aliases {
		cmd := ExecSudoCommand(ctx, sudoPassword, fmt.Sprintf("zypper modifyrepo -d '%s'", escapeSingleQuotes(alias)))
		repoOutput, err := cmd.CombinedOutput()
		if err != nil {
			o.log(fmt.Sprintf("Failed to disable install media repo %s: %s", alias, strings.TrimSpace(string(repoOutput))))
			return fmt.Errorf("failed to disable install media repo %s: %w", alias, err)
		}
		o.log(fmt.Sprintf("Disabled install media repo %s: %s", alias, strings.TrimSpace(string(repoOutput))))
	}

	return nil
}

func (o *OpenSUSEDistribution) zypperInstallArgs(packages []string, minimal bool) []string {
	args := []string{"zypper", "install", "-y"}
	if minimal {
		args = append(args, "--no-recommends")
	}
	return append(args, packages...)
}

func (o *OpenSUSEDistribution) installZypperPackages(ctx context.Context, packages []string, sudoPassword string, progressChan chan<- InstallProgressMsg, phase InstallPhase, step string, startProgress float64, endProgress float64) error {
	if len(packages) == 0 {
		return nil
	}

	o.log(fmt.Sprintf("Installing zypper packages: %s", strings.Join(packages, ", ")))

	groups := orderedMinimalInstallGroups(packages)
	totalGroups := len(groups)

	groupIndex := 0
	installGroup := func(groupPackages []string, minimal bool) error {
		if len(groupPackages) == 0 {
			return nil
		}

		groupIndex++
		groupStart := startProgress
		groupEnd := endProgress
		if totalGroups > 1 {
			midpoint := startProgress + ((endProgress - startProgress) / 2)
			if groupIndex == 1 {
				groupEnd = midpoint
			} else {
				groupStart = midpoint
			}
		}

		args := o.zypperInstallArgs(groupPackages, minimal)
		progressChan <- InstallProgressMsg{
			Phase:       phase,
			Progress:    groupStart,
			Step:        step,
			IsComplete:  false,
			NeedsSudo:   true,
			CommandInfo: fmt.Sprintf("sudo %s", strings.Join(args, " ")),
		}

		cmd := ExecSudoCommand(ctx, sudoPassword, strings.Join(args, " "))
		return o.runWithProgress(cmd, progressChan, phase, groupStart, groupEnd)
	}

	for _, group := range groups {
		if err := installGroup(group.packages, group.minimal); err != nil {
			return err
		}
	}
	return nil
}

func (o *OpenSUSEDistribution) installQuickshell(ctx context.Context, variant deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
	o.log("Installing quickshell from source (with openSUSE-specific build flags)...")

	homeDir := os.Getenv("HOME")
	if homeDir == "" {
		return fmt.Errorf("HOME environment variable not set")
	}

	cacheDir := filepath.Join(homeDir, ".cache", "dankinstall")
	if err := os.MkdirAll(cacheDir, 0o755); err != nil {
		return fmt.Errorf("failed to create cache directory: %w", err)
	}

	tmpDir := filepath.Join(cacheDir, "quickshell-build")
	if err := os.MkdirAll(tmpDir, 0o755); err != nil {
		return fmt.Errorf("failed to create temp directory: %w", err)
	}
	defer os.RemoveAll(tmpDir)

	progressChan <- InstallProgressMsg{
		Phase:       PhaseSystemPackages,
		Progress:    0.1,
		Step:        "Cloning quickshell repository...",
		IsComplete:  false,
		CommandInfo: "git clone https://github.com/quickshell-mirror/quickshell.git",
	}

	var cloneCmd *exec.Cmd
	if forceQuickshellGit || variant == deps.VariantGit {
		cloneCmd = exec.CommandContext(ctx, "git", "clone", "https://github.com/quickshell-mirror/quickshell.git", tmpDir)
	} else {
		latestTag := o.getLatestQuickshellTag(ctx)
		if latestTag != "" {
			o.log(fmt.Sprintf("Using latest quickshell tag: %s", latestTag))
			cloneCmd = exec.CommandContext(ctx, "git", "clone", "--branch", latestTag, "https://github.com/quickshell-mirror/quickshell.git", tmpDir)
		} else {
			o.log("Warning: failed to fetch latest tag, using default branch")
			cloneCmd = exec.CommandContext(ctx, "git", "clone", "https://github.com/quickshell-mirror/quickshell.git", tmpDir)
		}
	}
	if err := cloneCmd.Run(); err != nil {
		return fmt.Errorf("failed to clone quickshell: %w", err)
	}

	buildDir := tmpDir + "/build"
	if err := os.MkdirAll(buildDir, 0o755); err != nil {
		return fmt.Errorf("failed to create build directory: %w", err)
	}

	progressChan <- InstallProgressMsg{
		Phase:       PhaseSystemPackages,
		Progress:    0.3,
		Step:        "Configuring quickshell build (with openSUSE flags)...",
		IsComplete:  false,
		CommandInfo: "cmake -B build -S . -G Ninja",
	}

	// Get optflags from rpm
	optflagsCmd := exec.CommandContext(ctx, "rpm", "--eval", "%{optflags}")
	optflagsOutput, err := optflagsCmd.Output()
	optflags := strings.TrimSpace(string(optflagsOutput))
	if err != nil || optflags == "" {
		o.log("Warning: Could not get optflags from rpm, using default -O2 -g")
		optflags = "-O2 -g"
	}

	// Set openSUSE-specific CFLAGS
	customCFLAGS := fmt.Sprintf("%s -I/usr/include/wayland", optflags)

	configureCmd := exec.CommandContext(ctx, "cmake", "-GNinja", "-B", "build",
		"-DCMAKE_BUILD_TYPE=RelWithDebInfo",
		"-DCRASH_REPORTER=off",
		"-DCMAKE_CXX_STANDARD=20")
	configureCmd.Dir = tmpDir
	configureCmd.Env = append(os.Environ(),
		"TMPDIR="+cacheDir,
		"CFLAGS="+customCFLAGS,
		"CXXFLAGS="+customCFLAGS)

	o.log(fmt.Sprintf("Using CFLAGS: %s", customCFLAGS))

	output, err := configureCmd.CombinedOutput()
	if err != nil {
		o.log(fmt.Sprintf("cmake configure failed. Output:\n%s", string(output)))
		return fmt.Errorf("failed to configure quickshell: %w\nCMake output:\n%s", err, string(output))
	}

	o.log(fmt.Sprintf("cmake configure successful. Output:\n%s", string(output)))

	progressChan <- InstallProgressMsg{
		Phase:       PhaseSystemPackages,
		Progress:    0.4,
		Step:        "Building quickshell (this may take a while)...",
		IsComplete:  false,
		CommandInfo: "cmake --build build",
	}

	buildCmd := exec.CommandContext(ctx, "cmake", "--build", "build")
	buildCmd.Dir = tmpDir
	buildCmd.Env = append(os.Environ(),
		"TMPDIR="+cacheDir,
		"CFLAGS="+customCFLAGS,
		"CXXFLAGS="+customCFLAGS)
	if err := o.runWithProgressStep(buildCmd, progressChan, PhaseSystemPackages, 0.4, 0.8, "Building quickshell..."); err != nil {
		return fmt.Errorf("failed to build quickshell: %w", err)
	}

	progressChan <- InstallProgressMsg{
		Phase:       PhaseSystemPackages,
		Progress:    0.8,
		Step:        "Installing quickshell...",
		IsComplete:  false,
		NeedsSudo:   true,
		CommandInfo: "sudo cmake --install build",
	}

	installCmd := ExecSudoCommand(ctx, sudoPassword, "cmake --install build")
	installCmd.Dir = tmpDir
	if err := installCmd.Run(); err != nil {
		return fmt.Errorf("failed to install quickshell: %w", err)
	}

	o.log("quickshell installed successfully from source")
	return nil
}

func (o *OpenSUSEDistribution) installRust(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
	if o.commandExists("cargo") {
		return nil
	}

	progressChan <- InstallProgressMsg{
		Phase:       PhaseSystemPackages,
		Progress:    0.82,
		Step:        "Installing rustup...",
		IsComplete:  false,
		NeedsSudo:   true,
		CommandInfo: "sudo zypper install rustup",
	}

	rustupInstallCmd := ExecSudoCommand(ctx, sudoPassword, "zypper install -y rustup")
	if err := o.runWithProgress(rustupInstallCmd, progressChan, PhaseSystemPackages, 0.82, 0.83); err != nil {
		return fmt.Errorf("failed to install rustup: %w", err)
	}

	progressChan <- InstallProgressMsg{
		Phase:       PhaseSystemPackages,
		Progress:    0.83,
		Step:        "Installing stable Rust toolchain...",
		IsComplete:  false,
		CommandInfo: "rustup install stable",
	}

	rustInstallCmd := exec.CommandContext(ctx, "bash", "-c", "rustup install stable && rustup default stable")
	if err := o.runWithProgress(rustInstallCmd, progressChan, PhaseSystemPackages, 0.83, 0.84); err != nil {
		return fmt.Errorf("failed to install Rust toolchain: %w", err)
	}

	if !o.commandExists("cargo") {
		o.log("Warning: cargo not found in PATH after Rust installation, trying to source environment")
	}

	return nil
}

func (o *OpenSUSEDistribution) InstallManualPackages(ctx context.Context, packages []string, variantMap map[string]deps.PackageVariant, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
	if len(packages) == 0 {
		return nil
	}

	o.log(fmt.Sprintf("Installing manual packages: %s", strings.Join(packages, ", ")))

	for _, pkg := range packages {
		if pkg == "matugen" {
			if err := o.installRust(ctx, sudoPassword, progressChan); err != nil {
				return fmt.Errorf("failed to install Rust: %w", err)
			}
			break
		}
	}

	for _, pkg := range packages {
		variant := variantMap[pkg]
		if pkg == "quickshell" {
			if err := o.installQuickshell(ctx, variant, sudoPassword, progressChan); err != nil {
				return fmt.Errorf("failed to install quickshell: %w", err)
			}
		} else {
			if err := o.ManualPackageInstaller.InstallManualPackages(ctx, []string{pkg}, variantMap, sudoPassword, progressChan); err != nil {
				return fmt.Errorf("failed to install %s: %w", pkg, err)
			}
		}
	}

	return nil
}
