package installer

import (
	"context"
	"fmt"

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

// PlatformInstaller defines the interface for platform-specific installation methods
type PlatformInstaller interface {
	// GetAvailableMethods returns available installation methods
	GetAvailableMethods(filterInstalled bool) []InstallMethod

	// Installation methods with output
	InstallMPVWithOutput(cr *CommandRunner, method string, uiType string) error
	InstallMPCQTWithOutput(cr *CommandRunner, method string) error
	InstallMPVAppWithOutput(cr *CommandRunner, uiType string) error
	InstallMPVViaBrewWithOutput(cr *CommandRunner, uiType string, isUpdate bool) error
	InstallIINAWithOutput(cr *CommandRunner) error
	InstallFlatpakWithOutput(cr *CommandRunner, flatpakId string, uiType string, isUpdate bool) error
	InstallMPVViaPackageWithOutput(cr *CommandRunner, uiType string, isUpdate bool) error
	InstallCelluloidViaPackageWithOutput(cr *CommandRunner, isUpdate bool) error

	// Update methods
	UpdateMPVWithOutput(cr *CommandRunner, method string) error
	UpdateMPVAppWithOutput(cr *CommandRunner) error

	// Uninstall methods with output
	UninstallFlatpakWithOutput(cr *CommandRunner, appId string) error
	UninstallViaPackageWithOutput(cr *CommandRunner, appName string) error
	UninstallBrewWithOutput(cr *CommandRunner, appName string) error
	UninstallAppWithOutput(cr *CommandRunner, appName string) error
	UninstallWithOutput(cr *CommandRunner) error
	UninstallMPCQTWithOutput(cr *CommandRunner) error

	// Windows-specific methods
	SetupFileAssociationsWithOutput(cr *CommandRunner) error
	RemoveFileAssociationsWithOutput(cr *CommandRunner) error
	CreateInstallerShortcutWithOutput(cr *CommandRunner) error
	CreateInstallerShortcutToBinaryWithOutput(cr *CommandRunner, binaryPath string) error
	CreateWebUIShortcutWithOutput(cr *CommandRunner) error
	CreateMPVShortcut(cr *CommandRunner) error
}

// InstallationHandler provides unified method handling across platforms
type InstallationHandler struct {
	Platform          *platform.Platform
	Installer         *Installer
	PlatformInstaller PlatformInstaller
}

// ExecuteInstall executes an installation method based on methodID
// The context can be used to cancel long-running operations
func (ih *InstallationHandler) ExecuteInstall(ctx context.Context, cr *CommandRunner, methodID string, uiType string, isUpdate bool) error {
	// Check for cancellation at the start
	select {
	case <-ctx.Done():
		return fmt.Errorf("installation cancelled: %w", ctx.Err())
	default:
	}

	// Set context on command runner
	cr.SetContext(ctx)

	// Handle component methods (UOSC, ModernZ, FFmpeg) - these don't need platform installers
	switch methodID {
	case constants.MethodUOSC:
		installDir := config.GetMPVConfigPath()
		if isUpdate {
			return ih.Installer.UpdateUOSCWithOutput(cr, installDir)
		}
		return ih.Installer.InstallUOSCWithOutput(cr, installDir)

	case constants.MethodModernZ:
		installDir := config.GetMPVConfigPath()
		if isUpdate {
			return ih.Installer.UpdateModernZWithOutput(cr, installDir)
		}
		return ih.Installer.InstallModernZWithOutput(cr, installDir)

	case constants.MethodFFmpeg:
		if !ih.Platform.IsWindows() {
			return fmt.Errorf("FFmpeg component updates are only supported on Windows")
		}
		installDir := config.GetInstallPath()
		return ih.Installer.UpdateFFmpegWithOutput(cr, installDir)
	}

	switch methodID {
	// Windows methods
	case constants.MethodMPVBinary, constants.MethodMPVBinaryV3:
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Windows installer not available on %s", ih.Platform.OSType)
		}
		if isUpdate {
			return ih.PlatformInstaller.UpdateMPVWithOutput(cr, methodID)
		}
		return ih.PlatformInstaller.InstallMPVWithOutput(cr, methodID, uiType)

	case constants.MethodMPCQT:
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("MPC-QT installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.InstallMPCQTWithOutput(cr, methodID)

	// macOS methods
	case constants.MethodMPVApp:
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("macOS installer not available on %s", ih.Platform.OSType)
		}
		if isUpdate {
			return ih.PlatformInstaller.UpdateMPVAppWithOutput(cr)
		}
		return ih.PlatformInstaller.InstallMPVAppWithOutput(cr, uiType)

	case constants.MethodMPVBrew:
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("macOS installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.InstallMPVViaBrewWithOutput(cr, uiType, isUpdate)

	case constants.MethodIINA:
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("macOS installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.InstallIINAWithOutput(cr)

	// Linux Flatpak methods
	case constants.MethodMPVFlatpak:
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.InstallFlatpakWithOutput(cr, constants.FlatpakMPV, uiType, isUpdate)

	case constants.MethodCelluloidFlatpak:
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType)
		}
		// Celluloid has its own UI, don't install UOSC/ModernZ
		return ih.PlatformInstaller.InstallFlatpakWithOutput(cr, constants.FlatpakCelluloid, constants.UITypeNone, isUpdate)

	// Linux Package Manager methods
	case constants.MethodMPVPackage:
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.InstallMPVViaPackageWithOutput(cr, uiType, isUpdate)

	case constants.MethodCelluloidPackage:
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.InstallCelluloidViaPackageWithOutput(cr, isUpdate)

	default:
		return fmt.Errorf("unknown installation method: %s", methodID)
	}
}

// ExecuteUninstall executes an uninstallation method based on app info
// The context can be used to cancel long-running operations
func (ih *InstallationHandler) ExecuteUninstall(ctx context.Context, cr *CommandRunner, app *config.InstalledApp) error {
	// Check for cancellation at the start
	select {
	case <-ctx.Done():
		return fmt.Errorf("uninstallation cancelled: %w", ctx.Err())
	default:
	}

	// Set context on command runner
	cr.SetContext(ctx)

	methodID := app.InstallMethod

	// Windows methods
	if methodID == constants.MethodMPVBinary || methodID == constants.MethodMPVBinaryV3 {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Windows installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.UninstallWithOutput(cr)
	}

	if methodID == constants.MethodMPCQT {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Windows installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.UninstallMPCQTWithOutput(cr)
	}

	// macOS methods
	if methodID == constants.MethodMPVApp {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("macOS installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.UninstallAppWithOutput(cr, "MPV")
	}

	if methodID == constants.MethodIINA {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("macOS installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.UninstallAppWithOutput(cr, "IINA")
	}

	if methodID == constants.MethodMPVBrew {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("macOS installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.UninstallBrewWithOutput(cr, "mpv")
	}

	// Linux Flatpak methods
	if methodID == constants.MethodMPVFlatpak {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.UninstallFlatpakWithOutput(cr, constants.FlatpakMPV)
	}

	if methodID == constants.MethodCelluloidFlatpak {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.UninstallFlatpakWithOutput(cr, constants.FlatpakCelluloid)
	}

	// Linux Package Manager methods
	if methodID == constants.MethodMPVPackage {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.UninstallViaPackageWithOutput(cr, constants.PackageMPV)
	}

	if methodID == constants.MethodCelluloidPackage {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Linux installer not available on %s", ih.Platform.OSType)
		}
		return ih.PlatformInstaller.UninstallViaPackageWithOutput(cr, constants.PackageCelluloid)
	}

	return fmt.Errorf("unknown installation method: %s", methodID)
}

// ExecuteUpdate executes an update operation based on methodID
// This is a convenience wrapper around ExecuteInstall with isUpdate=true
func (ih *InstallationHandler) ExecuteUpdate(ctx context.Context, cr *CommandRunner, methodID string) error {
	return ih.ExecuteInstall(ctx, cr, methodID, constants.DefaultUIType, true)
}

// SetupFileAssociationsWithOutput creates file association shortcuts on Desktop
// This is Windows-specific and creates shortcuts that user runs as Administrator
func (ih *InstallationHandler) SetupFileAssociationsWithOutput(cr *CommandRunner) error {
	if ih.Platform.IsWindows() {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Windows installer not available")
		}
		return ih.PlatformInstaller.SetupFileAssociationsWithOutput(cr)
	}
	return fmt.Errorf("file associations are only supported on Windows")
}

// RemoveFileAssociationsWithOutput removes file associations
// This is Windows-specific and runs the uninstall batch file
func (ih *InstallationHandler) RemoveFileAssociationsWithOutput(cr *CommandRunner) error {
	if ih.Platform.IsWindows() {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Windows installer not available")
		}
		return ih.PlatformInstaller.RemoveFileAssociationsWithOutput(cr)
	}
	return fmt.Errorf("file associations are only supported on Windows")
}

// CreateInstallerShortcutWithOutput creates a Desktop shortcut to the installer binary
// This is Windows-specific and creates a shortcut for easy access to the installer
func (ih *InstallationHandler) CreateInstallerShortcutWithOutput(cr *CommandRunner) error {
	if ih.Platform.IsWindows() {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Windows installer not available")
		}
		return ih.PlatformInstaller.CreateInstallerShortcutWithOutput(cr)
	}
	if ih.Platform.IsDarwin() {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("macOS installer not available")
		}
		return ih.PlatformInstaller.CreateInstallerShortcutWithOutput(cr)
	}
	if ih.Platform.IsLinux() {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Linux installer not available")
		}
		return ih.PlatformInstaller.CreateInstallerShortcutWithOutput(cr)
	}
	return fmt.Errorf("installer shortcut creation not supported")
}

// CreateWebUIShortcutWithOutput creates a shortcut to launch installer in Web UI mode
// Creates platform-specific shortcuts (Windows .lnk, Linux .desktop, macOS .app)
func (ih *InstallationHandler) CreateWebUIShortcutWithOutput(cr *CommandRunner) error {
	if ih.Platform.IsWindows() {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Windows installer not available")
		}
		return ih.PlatformInstaller.CreateWebUIShortcutWithOutput(cr)
	} else if ih.Platform.IsDarwin() {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("macOS installer not available")
		}
		return ih.PlatformInstaller.CreateWebUIShortcutWithOutput(cr)
	} else if ih.Platform.IsLinux() {
		if ih.PlatformInstaller == nil {
			return fmt.Errorf("Linux installer not available")
		}
		return ih.PlatformInstaller.CreateWebUIShortcutWithOutput(cr)
	}
	return fmt.Errorf("Web UI shortcut creation not supported on this platform")
}

// CreateMPVShortcutWithOutput creates Desktop and Start Menu shortcuts for MPV
// This is Windows-specific
func (ih *InstallationHandler) CreateMPVShortcutWithOutput(cr *CommandRunner) error {
	if !ih.Platform.IsWindows() {
		return fmt.Errorf("MPV shortcuts are only supported on Windows")
	}
	if ih.PlatformInstaller == nil {
		return fmt.Errorf("Windows installer not available")
	}
	return ih.PlatformInstaller.CreateMPVShortcut(cr)
}
