//go:build linux

package installer

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

	"gitgud.io/mike/mpv-manager/pkg/constants"
	"gitgud.io/mike/mpv-manager/pkg/log"
)

// InstallMPVViaPackage installs MPV using the system package manager.
func (li *LinuxInstaller) InstallMPVViaPackage(installUOSC bool) error {
	switch li.Platform.DistroFamily {
	case "debian":
		return li.installViaAPT(installUOSC)
	case "rhel":
		return li.installViaDNF(installUOSC)
	case "arch":
		return li.installViaPacman(installUOSC)
	default:
		return fmt.Errorf("unsupported distro family: %s", li.Platform.DistroFamily)
	}
}

// InstallCelluloidViaPackage installs Celluloid using the system package manager.
func (li *LinuxInstaller) InstallCelluloidViaPackage() error {
	fmt.Println("Installing Celluloid via package manager...")
	fmt.Println("Note: You may be prompted for your password.")
	prefix := li.getPrivilegePrefix()

	switch li.Platform.DistroFamily {
	case "debian":
		fmt.Println("Updating package list...")
		args := append(prefix, "apt", "update")
		cmd := exec.Command(args[0], args[1:]...)
		cmd.Stdin = os.Stdin
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("apt update failed: %w", err)
		}

		fmt.Println("Installing required dependencies...")
		args = append(prefix, "apt", "install", "-y", "software-properties-common")
		cmd = exec.Command(args[0], args[1:]...)
		cmd.Stdin = os.Stdin
		_ = cmd.Run()

		fmt.Println("Adding Celluloid PPA...")
		args = append(prefix, "add-apt-repository", "-y", "ppa:xuzhen666/gnome-mpv")
		cmd = exec.Command(args[0], args[1:]...)
		cmd.Stdin = os.Stdin
		_ = cmd.Run()

		fmt.Println("Updating package list...")
		args = append(prefix, "apt", "update")
		cmd = exec.Command(args[0], args[1:]...)
		cmd.Stdin = os.Stdin
		_ = cmd.Run()

		fmt.Println("Installing Celluloid...")
		args = append(prefix, "apt", "install", "-y", constants.PackageCelluloid)
		cmd = exec.Command(args[0], args[1:]...)
		cmd.Stdin = os.Stdin
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("apt install failed: %w", err)
		}
	case "rhel":
		fmt.Println("Installing Celluloid via dnf...")
		args := append(prefix, "dnf", "install", "-y", constants.PackageCelluloid)
		cmd := exec.Command(args[0], args[1:]...)
		cmd.Stdin = os.Stdin
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("dnf install failed: %w", err)
		}
	case "arch":
		fmt.Println("Installing Celluloid via pacman...")
		args := append(prefix, "pacman", "-S", "--noconfirm", constants.PackageCelluloid)
		cmd := exec.Command(args[0], args[1:]...)
		cmd.Stdin = os.Stdin
		if err := cmd.Run(); err != nil {
			return fmt.Errorf("pacman install failed: %w", err)
		}
	default:
		return fmt.Errorf("unsupported distro family: %s", li.Platform.DistroFamily)
	}
	return nil
}

// installViaAPT installs MPV using apt (Debian/Ubuntu).
func (li *LinuxInstaller) installViaAPT(installUOSC bool) error {
	fmt.Println("Installing MPV via apt...")
	fmt.Println("Note: You may be prompted for your password.")
	prefix := li.getPrivilegePrefix()
	args := append(prefix, "apt", "update")
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdin = os.Stdin
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("apt update failed: %w", err)
	}

	args = append(prefix, "apt", "install", "-y", constants.PackageMPV)
	cmd = exec.Command(args[0], args[1:]...)
	cmd.Stdin = os.Stdin
	err := cmd.Run()
	if err != nil {
		return fmt.Errorf("apt install failed: %w", err)
	}
	return nil
}

// installViaDNF installs MPV using dnf (Fedora/RHEL).
func (li *LinuxInstaller) installViaDNF(installUOSC bool) error {
	fmt.Println("Installing MPV via dnf...")
	fmt.Println("Note: You may be prompted for your password.")
	prefix := li.getPrivilegePrefix()
	args := append(prefix, "dnf", "install", "-y", constants.PackageMPV)
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdin = os.Stdin
	err := cmd.Run()
	if err != nil {
		return fmt.Errorf("dnf install failed: %w", err)
	}
	return nil
}

// installViaPacman installs MPV using pacman (Arch Linux).
func (li *LinuxInstaller) installViaPacman(installUOSC bool) error {
	fmt.Println("Installing MPV via pacman...")
	fmt.Println("Note: You may be prompted for your password.")
	prefix := li.getPrivilegePrefix()
	args := append(prefix, "pacman", "-S", "--noconfirm", constants.PackageMPV)
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdin = os.Stdin
	err := cmd.Run()
	if err != nil {
		return fmt.Errorf("pacman install failed: %w", err)
	}
	return nil
}

// UninstallViaPackage uninstalls an application using the system package manager.
func (li *LinuxInstaller) UninstallViaPackage(appName string) error {
	prefix := li.getPrivilegePrefix()

	switch li.Platform.DistroFamily {
	case "debian":
		args := append(prefix, "apt", "remove", "-y", appName)
		cmd := exec.Command(args[0], args[1:]...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		return cmd.Run()
	case "rhel":
		args := append(prefix, "dnf", "remove", "-y", appName)
		cmd := exec.Command(args[0], args[1:]...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		return cmd.Run()
	case "arch":
		args := append(prefix, "pacman", "-Rns", "--noconfirm", appName)
		cmd := exec.Command(args[0], args[1:]...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		return cmd.Run()
	default:
		return fmt.Errorf("unsupported distro family: %s", li.Platform.DistroFamily)
	}
}

// InstallMPVViaPackageWithOutput installs MPV using the system package manager with output streaming.
func (li *LinuxInstaller) InstallMPVViaPackageWithOutput(cr *CommandRunner, uiType string, isUpdate bool) error {
	family := li.Platform.DistroFamily
	if family == "" {
		family = detectLinuxFamily()
	}

	packageName := constants.PackageMPV

	if isUpdate {
		cr.outputChan <- fmt.Sprintf("Updating MPV via package manager (family: %s)...", family)

		pm := NewPackageManager(family)
		// Build commands WITHOUT sudo prefix - RunSudoCommand handles authentication
		updateCmd := pm.BuildUpdateCommand(false)
		upgradeCmd := pm.BuildUpgradeCommand(packageName, false)

		if cr != nil {
			cr.outputChan <- "Updating package metadata..."
			cr.outputChan <- fmt.Sprintf("Running: sudo %s", strings.Join(updateCmd, " "))
		}
		if err := cr.RunSudoCommand(updateCmd[0], updateCmd[1:]...); err != nil {
			log.Warn("Failed to update package metadata, continuing...")
		}

		if cr != nil {
			cr.outputChan <- "Updating MPV..."
			cr.outputChan <- fmt.Sprintf("Running: sudo %s", strings.Join(upgradeCmd, " "))
		}
		if err := cr.RunSudoCommand(upgradeCmd[0], upgradeCmd[1:]...); err != nil {
			return err
		}

		cr.outputChan <- constants.MsgConfigNotOverwritten
		return nil
	}

	cr.outputChan <- fmt.Sprintf("Installing MPV via package manager (family: %s)...", family)

	pm := NewPackageManager(family)
	// Build command WITHOUT sudo prefix - RunSudoCommand handles authentication
	installCmd := pm.BuildInstallCommand(packageName, false)

	if cr != nil {
		cr.outputChan <- "Running: sudo " + strings.Join(installCmd, " ")
	}
	if err := cr.RunSudoCommand(installCmd[0], installCmd[1:]...); err != nil {
		return err
	}

	if uiType != constants.UITypeNone {
		homeDir, err := os.UserHomeDir()
		if err != nil {
			log.Warn(fmt.Sprintf("Failed to get home directory: %v", err))
		} else {
			configDir := GetMPVConfigDirFromHome(homeDir)
			InstallUISafely(li.Installer, cr, configDir, uiType)
		}
	}

	// Install MPV config with platform-specific defaults
	if err := InstallMPVConfigWithPlatformDefaults(li.Installer, cr, li.Platform.OSType, li.Platform.GPUInfo.Brand); err != nil {
		cr.outputChan <- fmt.Sprintf("Warning: Failed to install MPV config: %v", err)
	}

	return nil
}

// InstallCelluloidViaPackageWithOutput installs Celluloid using the system package manager with output streaming.
func (li *LinuxInstaller) InstallCelluloidViaPackageWithOutput(cr *CommandRunner, isUpdate bool) error {
	family := li.Platform.DistroFamily
	if family == "" {
		family = detectLinuxFamily()
	}

	packageName := constants.PackageCelluloid

	if isUpdate {
		cr.outputChan <- fmt.Sprintf("Updating Celluloid via package manager (family: %s)...", family)

		pm := NewPackageManager(family)
		// Build commands WITHOUT sudo prefix - RunSudoCommand handles authentication
		updateCmd := pm.BuildUpdateCommand(false)
		upgradeCmd := pm.BuildUpgradeCommand(packageName, false)

		if cr != nil {
			cr.outputChan <- "Updating package metadata..."
			cr.outputChan <- fmt.Sprintf("Running: sudo %s", strings.Join(updateCmd, " "))
		}
		if err := cr.RunSudoCommand(updateCmd[0], updateCmd[1:]...); err != nil {
			log.Warn("Failed to update package metadata, continuing...")
		}

		if cr != nil {
			cr.outputChan <- "Updating Celluloid..."
			cr.outputChan <- fmt.Sprintf("Running: sudo %s", strings.Join(upgradeCmd, " "))
		}
		if err := cr.RunSudoCommand(upgradeCmd[0], upgradeCmd[1:]...); err != nil {
			return err
		}

		return nil
	}

	cr.outputChan <- fmt.Sprintf("Installing Celluloid via package manager (family: %s)...", family)

	pm := NewPackageManager(family)
	// Build commands WITHOUT sudo prefix - RunSudoCommand handles authentication
	updateCmd := pm.BuildUpdateCommand(false)
	installCmd := pm.BuildInstallCommand(packageName, false)

	if cr != nil {
		cr.outputChan <- "Updating package list..."
		cr.outputChan <- fmt.Sprintf("Running: sudo %s", strings.Join(updateCmd, " "))
	}
	if err := cr.RunSudoCommand(updateCmd[0], updateCmd[1:]...); err != nil {
		log.Warn("Failed to update package list, continuing...")
	}

	if cr != nil {
		cr.outputChan <- "Installing Celluloid..."
		cr.outputChan <- fmt.Sprintf("Running: sudo %s", strings.Join(installCmd, " "))
	}
	if err := cr.RunSudoCommand(installCmd[0], installCmd[1:]...); err != nil {
		return err
	}

	// Install MPV config with platform-specific defaults
	if err := InstallMPVConfigWithPlatformDefaults(li.Installer, cr, li.Platform.OSType, li.Platform.GPUInfo.Brand); err != nil {
		log.Warn(fmt.Sprintf("Failed to install MPV config: %v", err))
	}
	if err := li.ConfigureCelluloidPackageWithOutput(cr); err != nil {
		log.Warn(fmt.Sprintf("Failed to configure Celluloid: %v", err))
	}

	return nil
}

// isMPVInstall checks if the given install ID corresponds to an MPV installation.
func isMPVInstall(installID string) bool {
	mpvInstalls := []string{constants.FlatpakMPV, constants.PackageMPV, constants.FlatpakMPV}
	for _, mpvID := range mpvInstalls {
		if installID == mpvID {
			return true
		}
	}
	return false
}

// detectLinuxFamily detects the Linux distribution family from /etc/os-release.
func detectLinuxFamily() string {
	if data, err := os.ReadFile("/etc/os-release"); err == nil {
		content := string(data)
		if strings.Contains(content, "debian") || strings.Contains(content, "ubuntu") {
			return "debian"
		} else if strings.Contains(content, "rhel") || strings.Contains(content, "fedora") {
			return "rhel"
		} else if strings.Contains(content, "arch") {
			return "arch"
		}
	}
	return ""
}

// getPackageManagerName returns the name of the package manager for the current distro.
func (li *LinuxInstaller) getPackageManagerName() string {
	family := li.Platform.DistroFamily
	if family == "" {
		family = detectLinuxFamily()
	}

	if family == "arch" {
		return "pacman"
	} else if family == "debian" {
		return "apt"
	} else if family == "rhel" {
		return "dnf"
	}
	return "package-manager"
}

// UninstallViaPackageWithOutput uninstalls an application using the system package manager with output streaming.
func (li *LinuxInstaller) UninstallViaPackageWithOutput(cr *CommandRunner, appName string) error {
	cr.outputChan <- fmt.Sprintf("Uninstalling %s via package manager...", appName)

	packageManager := NewPackageManager(li.Platform.DistroFamily)
	// Build command WITHOUT sudo prefix - RunSudoCommand handles authentication
	removeCmd := packageManager.BuildRemoveCommand(appName, false)

	if cr != nil {
		cr.outputChan <- fmt.Sprintf("Running: sudo %s", strings.Join(removeCmd, " "))
	}

	// Use RunSudoCommand which handles keyring password authentication
	if err := cr.RunSudoCommand(removeCmd[0], removeCmd[1:]...); err != nil {
		return fmt.Errorf("package remove failed: %w", err)
	}

	return nil
}
