//go:build windows

package installer

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

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

func (wi *WindowsInstaller) ensureShortcutsAssets(cr *CommandRunner) error {
	installerDir := filepath.Join(wi.InstallDir, constants.InstallerDir)
	// MPV's scripts are at the root of the install directory (zhongfly/mpv-winbuild)
	// constants.MPVInstallerDir is "" for zhongfly builds
	mpvScriptsDir := filepath.Join(wi.InstallDir, constants.MPVInstallerDir)

	// Ensure our mpv-manager directory exists
	if err := os.MkdirAll(installerDir, 0755); err != nil {
		return fmt.Errorf("failed to create mpv-manager directory: %w", err)
	}

	// Extract VBS script for shortcut creation
	vbsPath := filepath.Join(installerDir, "create-shortcut.vbs")
	if !wi.FileExists(vbsPath) {
		cr.outputChan <- "Extracting shortcut creation script..."
		if err := assets.ExtractCreateShortcutVBS(installerDir); err != nil {
			return fmt.Errorf("failed to extract VBS script: %w", err)
		}
	}

	// Copy MPV's register/unregister scripts from the install root to our directory
	// This allows the shortcuts to work and persists the scripts after MPV is updated
	mpvInstallBat := filepath.Join(mpvScriptsDir, constants.MPVInstallBat)
	ourInstallBat := filepath.Join(installerDir, constants.MPVInstallBat)
	if wi.FileExists(mpvInstallBat) && !wi.FileExists(ourInstallBat) {
		cr.outputChan <- "Copying file association scripts..."
		if err := copyFile(mpvInstallBat, ourInstallBat, 0755); err != nil {
			cr.outputChan <- fmt.Sprintf("Warning: Failed to copy %s: %v", constants.MPVInstallBat, err)
		}
	}

	mpvUninstallBat := filepath.Join(mpvScriptsDir, constants.MPVUninstallBat)
	ourUninstallBat := filepath.Join(installerDir, constants.MPVUninstallBat)
	if wi.FileExists(mpvUninstallBat) && !wi.FileExists(ourUninstallBat) {
		if err := copyFile(mpvUninstallBat, ourUninstallBat, 0755); err != nil {
			cr.outputChan <- fmt.Sprintf("Warning: Failed to copy %s: %v", constants.MPVUninstallBat, err)
		}
	}

	// NOTE: zhongfly/mpv-winbuild does not include .ico files.
	// We use mpv.exe as the icon source for shortcuts (it has an embedded icon).

	// Extract MPV Manager uninstall script
	managerUninstallBat := filepath.Join(installerDir, constants.UninstallBatFileName)
	if !wi.FileExists(managerUninstallBat) {
		if err := assets.ExtractUninstallBat(installerDir); err != nil {
			cr.outputChan <- fmt.Sprintf("Warning: Failed to extract %s: %v", constants.UninstallBatFileName, err)
			// Don't fail the entire operation for this
		}
	}

	return nil
}

func (wi *WindowsInstaller) createShortcut(cr *CommandRunner, shortcutPath, targetPath, workingDir, description, iconPath string) error {
	installerDir := filepath.Join(wi.InstallDir, constants.InstallerDir)
	vbsPath := filepath.Join(installerDir, "create-shortcut.vbs")

	cmd := exec.Command("wscript.exe", vbsPath, shortcutPath, targetPath, workingDir, description, iconPath)
	if output, err := cmd.CombinedOutput(); err != nil {
		return fmt.Errorf("failed to create shortcut: %w, output: %s", err, string(output))
	}

	return nil
}

func (wi *WindowsInstaller) createDesktopShortcut(cr *CommandRunner, shortcutName, targetPath, workingDir, description, iconPath string) error {
	homeDir, err := os.UserHomeDir()
	if err != nil {
		return fmt.Errorf("failed to get home directory: %w", err)
	}

	desktopPath := filepath.Join(homeDir, "Desktop")
	shortcutPath := filepath.Join(desktopPath, shortcutName)

	cr.outputChan <- fmt.Sprintf("Creating Desktop shortcut: %s", shortcutName)
	if err := wi.createShortcut(cr, shortcutPath, targetPath, workingDir, description, iconPath); err != nil {
		return err
	}

	return nil
}

func (wi *WindowsInstaller) createStartMenuShortcut(cr *CommandRunner, shortcutName, targetPath, workingDir, description, iconPath string) error {
	appData := os.Getenv("APPDATA")
	if appData == "" {
		return fmt.Errorf("could not get APPDATA path")
	}

	startMenuPath := filepath.Join(appData, "Microsoft", "Windows", "Start Menu", "Programs")
	shortcutPath := filepath.Join(startMenuPath, shortcutName)

	cr.outputChan <- fmt.Sprintf("Creating Start Menu shortcut: %s", shortcutName)
	if err := wi.createShortcut(cr, shortcutPath, targetPath, workingDir, description, iconPath); err != nil {
		return err
	}

	return nil
}

func (wi *WindowsInstaller) createFileAssociationShortcuts(cr *CommandRunner) error {
	installerDir := filepath.Join(wi.InstallDir, constants.InstallerDir)

	if err := wi.ensureShortcutsAssets(cr); err != nil {
		return err
	}

	installBat := filepath.Join(installerDir, constants.MPVInstallBat)

	// Use mpv.exe as icon source (zhongfly/mpv-winbuild doesn't include .ico files)
	mpvExePath := filepath.Join(wi.InstallDir, "mpv.exe")
	installIcon := mpvExePath
	if !wi.FileExists(mpvExePath) {
		installIcon = "" // Fallback to no icon
	}

	// Only create shortcut if the batch file exists
	if wi.FileExists(installBat) {
		if err := wi.createDesktopShortcut(
			cr,
			constants.ShortcutInstallName,
			installBat,
			installerDir,
			"Set MPV as Default Media Player (Run as Administrator)",
			installIcon,
		); err != nil {
			cr.outputChan <- fmt.Sprintf("Warning: Failed to create install shortcut: %v", err)
		}
	}

	uninstallBat := filepath.Join(installerDir, constants.MPVUninstallBat)

	// Only create shortcut if the batch file exists
	if wi.FileExists(uninstallBat) {
		if err := wi.createDesktopShortcut(
			cr,
			constants.ShortcutUninstallName,
			uninstallBat,
			installerDir,
			"Remove MPV from File Defaults (Run as Administrator)",
			installIcon,
		); err != nil {
			cr.outputChan <- fmt.Sprintf("Warning: Failed to create uninstall shortcut: %v", err)
		}
	}

	return nil
}

func (wi *WindowsInstaller) removeFileAssociationShortcuts(cr *CommandRunner) error {
	homeDir, err := os.UserHomeDir()
	if err != nil {
		return fmt.Errorf("failed to get home directory: %w", err)
	}

	desktopPath := filepath.Join(homeDir, "Desktop")

	installShortcut := filepath.Join(desktopPath, constants.ShortcutInstallName)
	if wi.FileExists(installShortcut) {
		cr.outputChan <- "Removing file association shortcut..."
		if err := os.Remove(installShortcut); err != nil {
			cr.outputChan <- fmt.Sprintf("Warning: Failed to remove %s: %v", constants.ShortcutInstallName, err)
		} else {
			cr.outputChan <- "Removed: " + constants.ShortcutInstallName
		}
	}

	uninstallShortcut := filepath.Join(desktopPath, constants.ShortcutUninstallName)
	if wi.FileExists(uninstallShortcut) {
		if err := os.Remove(uninstallShortcut); err != nil {
			cr.outputChan <- fmt.Sprintf("Warning: Failed to remove %s: %v", constants.ShortcutUninstallName, err)
		} else {
			cr.outputChan <- "Removed: " + constants.ShortcutUninstallName
		}
	}

	return nil
}

func (wi *WindowsInstaller) CreateMPVShortcut(cr *CommandRunner) error {
	mpvPath := filepath.Join(wi.InstallDir, "mpv.exe")
	if !wi.FileExists(mpvPath) {
		cr.outputChan <- "mpv.exe not found, skipping MPV shortcut"
		return nil
	}

	// Ensure VBS script and other assets are extracted
	if err := wi.ensureShortcutsAssets(cr); err != nil {
		return fmt.Errorf("failed to prepare shortcut assets: %w", err)
	}

	// Use MPV's built-in icon directly (exe provides its own icon)
	iconPath := mpvPath

	// Create Desktop shortcut
	if err := wi.createDesktopShortcut(
		cr,
		constants.ShortcutMPVName,
		mpvPath,
		wi.InstallDir,
		"MPV Media Player",
		iconPath,
	); err != nil {
		return err
	}

	// Create Start Menu shortcut
	if err := wi.createStartMenuShortcut(
		cr,
		constants.ShortcutMPVName,
		mpvPath,
		wi.InstallDir,
		"MPV Media Player",
		iconPath,
	); err != nil {
		cr.outputChan <- fmt.Sprintf("Warning: Failed to create Start Menu shortcut: %v", err)
		// Don't return error - desktop shortcut was created successfully
	}

	return nil
}

func (wi *WindowsInstaller) createInstallerShortcut(cr *CommandRunner) error {
	execPath, err := os.Executable()
	if err != nil {
		return fmt.Errorf("failed to get executable path: %w", err)
	}

	// Ensure VBS script and other assets are extracted
	if err := wi.ensureShortcutsAssets(cr); err != nil {
		return fmt.Errorf("failed to prepare shortcut assets: %w", err)
	}

	installerDir := filepath.Join(wi.InstallDir, constants.InstallerDir)
	if err := os.MkdirAll(installerDir, 0755); err != nil {
		return fmt.Errorf("failed to create installer dir: %w", err)
	}

	iconPath := filepath.Join(installerDir, constants.InstallerIco)
	if !wi.FileExists(iconPath) {
		if err := assets.ExtractInstallMPVIco(installerDir); err != nil {
			cr.outputChan <- fmt.Sprintf("Warning: Failed to extract icon: %v", err)
			iconPath = ""
		}
	}

	execDir := filepath.Dir(execPath)

	if err := wi.createDesktopShortcut(
		cr,
		constants.ShortcutInstallerName,
		execPath,
		execDir,
		"MPV.Rocks Installer - Manage MPV Player",
		iconPath,
	); err != nil {
		return err
	}

	return nil
}

func (wi *WindowsInstaller) createInstallerShortcutToBinary(cr *CommandRunner, binaryPath string) error {
	// Ensure VBS script and other assets are extracted
	if err := wi.ensureShortcutsAssets(cr); err != nil {
		return fmt.Errorf("failed to prepare shortcut assets: %w", err)
	}

	installerDir := filepath.Join(wi.InstallDir, constants.InstallerDir)
	if err := os.MkdirAll(installerDir, 0755); err != nil {
		return fmt.Errorf("failed to create installer dir: %w", err)
	}

	iconPath := filepath.Join(installerDir, constants.InstallerIco)
	if !wi.FileExists(iconPath) {
		if err := assets.ExtractInstallMPVIco(installerDir); err != nil {
			cr.outputChan <- fmt.Sprintf("Warning: Failed to extract icon: %v", err)
			iconPath = ""
		}
	}

	execDir := filepath.Dir(binaryPath)

	if err := wi.createDesktopShortcut(
		cr,
		constants.ShortcutInstallerName,
		binaryPath,
		execDir,
		"MPV.Rocks Installer - Manage MPV Player",
		iconPath,
	); err != nil {
		return err
	}

	return nil
}

func (wi *WindowsInstaller) CreateInstallerShortcutWithOutput(cr *CommandRunner) error {
	cr.outputChan <- "Creating MPV Manager shortcut on Desktop..."
	if err := wi.createInstallerShortcut(cr); err != nil {
		cr.outputChan <- fmt.Sprintf("Error creating shortcut: %v", err)
		return err
	}
	cr.outputChan <- ""
	cr.outputChan <- "✓ MPV Manager shortcut created successfully!"
	cr.outputChan <- ""
	cr.outputChan <- "Desktop shortcut created:"
	cr.outputChan <- "  • \"MPV Manager\""
	cr.outputChan <- ""
	cr.outputChan <- "You can now quickly launch the installer from your Desktop."
	return nil
}

func (wi *WindowsInstaller) CreateInstallerShortcutToBinaryWithOutput(cr *CommandRunner, binaryPath string) error {
	cr.outputChan <- "Creating MPV Manager shortcut on Desktop..."
	if err := wi.createInstallerShortcutToBinary(cr, binaryPath); err != nil {
		cr.outputChan <- fmt.Sprintf("Error creating shortcut: %v", err)
		return err
	}
	cr.outputChan <- ""
	cr.outputChan <- "✓ MPV Manager shortcut created successfully!"
	cr.outputChan <- ""
	cr.outputChan <- "Desktop shortcut created:"
	cr.outputChan <- "  • \"MPV Manager\""
	cr.outputChan <- ""
	cr.outputChan <- "You can now quickly launch the installer from your Desktop."
	return nil
}

func CreateWebUIShortcutWithOutput(cr *CommandRunner) error {
	execPath, err := os.Executable()
	if err != nil {
		return fmt.Errorf("failed to get executable path: %w", err)
	}

	cr.outputChan <- "Setting up MPV Manager..."

	homeDir, err := os.UserHomeDir()
	if err != nil {
		return fmt.Errorf("failed to get home directory: %w", err)
	}

	desktopPath := filepath.Join(homeDir, "Desktop")

	// Get the install directory from config (respects %APPDATA%\mpv for fresh installs)
	installBaseDir := config.GetInstallPath()
	installDir := filepath.Join(installBaseDir, constants.InstallerDir)
	if err := os.MkdirAll(installDir, 0755); err != nil {
		return fmt.Errorf("failed to create installer dir: %w", err)
	}

	// Copy executable to install directory if not already there or if different
	binaryPath := filepath.Join(installDir, "mpv-manager.exe")
	shouldCopy := false

	if _, err := os.Stat(binaryPath); os.IsNotExist(err) {
		shouldCopy = true
		cr.outputChan <- "Copying MPV Manager to user directory..."
	} else {
		// Check if the binary is different (e.g., newer version)
		currentExe, _ := os.ReadFile(execPath)
		existingBinary, _ := os.ReadFile(binaryPath)
		if !bytes.Equal(currentExe, existingBinary) {
			shouldCopy = true
			cr.outputChan <- "Updating MPV Manager binary..."
		}
	}

	if shouldCopy {
		input, err := os.ReadFile(execPath)
		if err != nil {
			return fmt.Errorf("failed to read executable: %w", err)
		}
		if err := os.WriteFile(binaryPath, input, 0755); err != nil {
			return fmt.Errorf("failed to copy executable: %w", err)
		}
		cr.outputChan <- "✓ MPV Manager binary copied"
	}

	// Extract icon
	iconPath := filepath.Join(installDir, constants.InstallerIco)
	if _, err := os.Stat(iconPath); os.IsNotExist(err) {
		if err := assets.ExtractInstallMPVIco(installDir); err != nil {
			cr.outputChan <- fmt.Sprintf("Warning: Failed to extract icon: %v", err)
			iconPath = ""
		}
	}

	// Extract uninstall script
	uninstallBat := filepath.Join(installDir, constants.UninstallBatFileName)
	if _, err := os.Stat(uninstallBat); os.IsNotExist(err) {
		if err := assets.ExtractUninstallBat(installDir); err != nil {
			cr.outputChan <- fmt.Sprintf("Warning: Failed to extract uninstall script: %v", err)
		}
	}

	// Create shortcut pointing to the copied binary
	shortcutPath := filepath.Join(desktopPath, constants.ShortcutInstallerName)
	targetPath := binaryPath
	arguments := "-m web"
	workingDir := installDir

	vbsScript := `
Set WshShell = CreateObject("WScript.Shell")
Set Shortcut = WshShell.CreateShortcut("` + shortcutPath + `")
Shortcut.TargetPath = "` + targetPath + `"
Shortcut.Arguments = "` + arguments + `"
Shortcut.WorkingDirectory = "` + workingDir + `"
Shortcut.Description = "MPV.Rocks Installer - Web UI Mode"
If "` + iconPath + `" <> "" Then
    Shortcut.IconLocation = "` + iconPath + `"
End If
Shortcut.Save
`

	vbsPath := filepath.Join(installDir, "create-webui-shortcut.vbs")
	if err := os.WriteFile(vbsPath, []byte(vbsScript), 0644); err != nil {
		return fmt.Errorf("failed to write VBS script: %w", err)
	}

	cmd := exec.Command("cscript", "//NoLogo", vbsPath)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("failed to create shortcut: %w", err)
	}

	cr.outputChan <- "✓ Desktop shortcut created: " + constants.ShortcutInstallerName

	return nil
}

func (wi *WindowsInstaller) CreateWebUIShortcutWithOutput(cr *CommandRunner) error {
	return CreateWebUIShortcutWithOutput(cr)
}

func (wi *WindowsInstaller) removeMPVShortcut(cr *CommandRunner) error {
	homeDir, err := os.UserHomeDir()
	if err != nil {
		return fmt.Errorf("failed to get home directory: %w", err)
	}

	// Remove Desktop shortcut
	desktopPath := filepath.Join(homeDir, "Desktop")
	mpvShortcut := filepath.Join(desktopPath, constants.ShortcutMPVName)

	if wi.FileExists(mpvShortcut) {
		cr.outputChan <- "Removing MPV Desktop shortcut..."
		if err := os.Remove(mpvShortcut); err != nil {
			cr.outputChan <- fmt.Sprintf("Warning: Failed to remove %s: %v", constants.ShortcutMPVName, err)
		} else {
			cr.outputChan <- "Removed: " + constants.ShortcutMPVName + " (Desktop)"
		}
	}

	// Remove Start Menu shortcut
	appData := os.Getenv("APPDATA")
	if appData != "" {
		startMenuPath := filepath.Join(appData, "Microsoft", "Windows", "Start Menu", "Programs")
		startMenuShortcut := filepath.Join(startMenuPath, constants.ShortcutMPVName)

		if wi.FileExists(startMenuShortcut) {
			cr.outputChan <- "Removing MPV Start Menu shortcut..."
			if err := os.Remove(startMenuShortcut); err != nil {
				cr.outputChan <- fmt.Sprintf("Warning: Failed to remove Start Menu shortcut: %v", err)
			} else {
				cr.outputChan <- "Removed: " + constants.ShortcutMPVName + " (Start Menu)"
			}
		}
	}

	return nil
}
