package web

import (
	"encoding/json"
	"fmt"
	"html/template"
	"strings"

	"gitgud.io/mike/mpv-manager/pkg/config"
	"gitgud.io/mike/mpv-manager/pkg/installer"
	"gitgud.io/mike/mpv-manager/pkg/locale"
	"gitgud.io/mike/mpv-manager/pkg/platform"
	"gitgud.io/mike/mpv-manager/pkg/version"
)

type DashboardData struct {
	Title              string
	AppVersion         string
	Platform           *platform.Platform
	InstalledApps      []config.InstalledApp
	Updates            *UpdateStatus
	Config             *ConfigSettings
	ConfigBackups      []ConfigBackup
	InstallMethods     []installer.InstallMethod
	InstalledAppGroups map[string][]AppGroupInfo
	ManagerUpdate      *ManagerUpdateInfo
	ClientUpdates      []ClientUpdateInfo
	HWAOptions         []string
	CurrentHWA         string
	RecommendedHWA     string
	Nav                []NavItem
	SystemInfo         *SystemInfo
	SelfUpdateDisabled bool // True when built with ldflag for package managers (Arch AUR, etc.)
}

func IsSelfUpdateDisabled() bool {
	return version.SelfUpdateDisabled == "true"
}

type AppGroupInfo struct {
	App              config.InstalledApp
	Logo             string
	TypeTag          string
	TypeLogo         string // Logo for the install method type (flatpak, snap, portable, etc.)
	UpdateAvailable  bool
	UpdateChecked    bool
	CurrentVersion   string
	AvailableVersion string
	Platform         *platform.Platform
	AppName          string
	AppType          string
	InstallMethod    string
	UIType           string // "uosc", "modernz", or empty
	Version          string // Installed version from config
	Managed          bool   // true if installed/configured by MPV Manager, false if externally detected
}

// AppCardTemplateData is used to pass app and platform data to the app-card template
type AppCardTemplateData struct {
	App      AppGroupInfo
	Platform *platform.Platform
}

type ManagerUpdateInfo struct {
	CurrentVersion  string
	LatestVersion   string
	UpdateAvailable bool
}

// SystemInfo holds application and environment information for the system page
type SystemInfo struct {
	// Application info
	AppName        string
	AppVersion     string
	GoVersion      string
	BuildTime      string
	RepositoryURL  string
	RepositoryName string

	// Environment paths (platform-specific)
	InstallPath   string
	MPVConfigPath string
	LogPath       string
	BackupPath    string
}

type ClientUpdateInfo struct {
	AppName          string
	CurrentVersion   string
	AvailableVersion string
	MethodID         string
	Logo             string
	UpdateAvailable  bool
	PackageManager   string
}

type UpdateStatus struct {
	ManagerUpdateAvailable bool
	ManagerCurrentVersion  string
	ManagerLatestVersion   string
	AppUpdatesAvailable    int
	Apps                   []AppUpdateInfo
}

type AppUpdateInfo struct {
	AppName          string
	CurrentVersion   string
	AvailableVersion string
}

type ConfigBackup struct {
	Filename    string
	CreatedDate string
	FilePath    string
}

type ConfigSettings struct {
	AudioLanguage      string
	AudioLanguages     []string
	SubtitleLanguage   string
	SubtitleLanguages  []string
	HardwareAccel      string
	VideoOutputDriver  string
	VideoScaleFilter   string
	DitherAlgorithm    string
	SavePositionOnQuit string
	Border             string
	Profile            string
	ConfigFile         string

	// Screenshot settings (NEW)
	ScreenshotFormat         string
	ScreenshotTagColorspace  string
	ScreenshotHighBitDepth   string
	ScreenshotTemplate       string
	ScreenshotDirectory      string
	ScreenshotJpegQuality    string
	ScreenshotPngCompression string
	ScreenshotWebpLossless   string
	ScreenshotWebpQuality    string
	ScreenshotJxlDistance    string
	ScreenshotAvifEncoder    string
	ScreenshotAvifPixfmt     string
	ScreenshotAvifOpts       string
}

type LanguagesData struct {
	Title                 string
	AppVersion            string
	Platform              *platform.Platform
	Config                *ConfigSettings
	CommonLanguages       []locale.LocaleEntry
	AllLanguageOptions    []LanguageOption
	CommonLanguageOptions []LanguageOption
	Nav                   []NavItem
}

type NavItem struct {
	Icon   template.HTML
	Name   string
	URL    string
	Active bool
}

var NavItems = []NavItem{
	{Icon: template.HTML("<i class='bx bx-dashboard'></i>"), Name: "Dashboard", URL: "/dashboard", Active: true},
	{Icon: template.HTML("<i class='bx bx-film'></i>"), Name: "MPV Player Apps", URL: "/apps"},
	{Icon: template.HTML("<i class='bx bx-bolt'></i>"), Name: "Hardware Acceleration", URL: "/hwaccel"},
	{Icon: template.HTML("<i class='bx bx-cog'></i>"), Name: "MPV Config Management", URL: "/config"},
	{Icon: template.HTML("<i class='bx bx-globe'></i>"), Name: "Language Preferences", URL: "/languages"},
	{Icon: template.HTML("<i class='bx bx-keyboard'></i>"), Name: "Keyboard Shortcuts", URL: "/hotkeys"},
	{Icon: template.HTML("<i class='bx bx-microchip'></i>"), Name: "System Information", URL: "/system"},
	{Icon: template.HTML("<i class='bx bx-terminal'></i>"), Name: "Logs Viewer", URL: "/logs"},
	{Icon: template.HTML("<i class='bx bx-slider-alt'></i>"), Name: "Manager Settings", URL: "/settings"},
}

func getCodecColor(codec string) string {
	codec = strings.ToLower(codec)
	switch codec {
	case "av1":
		return "text-amber-500"
	case "av2":
		return "text-pink-700"
	case "vvc":
		return "text-blue-500"
	case "hevc":
		return "text-blue-400"
	case "vp9":
		return "text-green-500"
	case "vp8":
		return "text-green-400"
	case "avc":
		return "text-red-600"
	case "mpeg2", "mpeg-2":
		return "text-purple-600"
	case "vc1", "vc-1":
		return "text-orange-600"
	case "prores":
		return "text-white"
	default:
		return "text-gray-400"
	}
}

func formatCodecName(codec string) string {
	codec = strings.ToUpper(codec)
	switch codec {
	case "MPEG2":
		return "MPEG-2"
	case "VC1":
		return "VC-1"
	default:
		return codec
	}
}

func getCodecBadgeColor(codec string) string {
	codec = strings.ToLower(codec)
	switch codec {
	case "av1":
		return "bg-amber-500"
	case "av2":
		return "bg-pink-700"
	case "vvc":
		return "bg-blue-500"
	case "hevc":
		return "bg-blue-600"
	case "vp9":
		return "bg-green-600"
	case "vp8":
		return "bg-green-500"
	case "avc":
		return "bg-red-600"
	case "mpeg2", "mpeg-2":
		return "bg-purple-600"
	case "vc1", "vc-1":
		return "bg-orange-600"
	case "prores":
		return "bg-black text-white"
	default:
		return "bg-gray-600"
	}
}

func LanguageName(code string) string {
	if code == "" {
		return "Not set"
	}
	entries, err := LoadLocales()
	if err != nil {
		return code
	}
	langEntry := locale.FindByLanguageCode(code, entries)
	if langEntry != nil {
		return langEntry.LanguageName
	}
	return code
}

func getVendorColor(vendor string) string {
	vendor = strings.ToLower(vendor)
	switch vendor {
	case "intel", "genuineintel":
		return "text-blue-400"
	case "amd", "authenticamd":
		return "text-red-400"
	case "nvidia":
		return "text-green-400"
	case "apple":
		return "apple-gradient"
	default:
		return "text-white"
	}
}

func getVendorClass(vendor string) string {
	vendor = strings.ToLower(vendor)
	switch vendor {
	case "intel", "genuineintel":
		return "text-blue-400"
	case "amd", "authenticamd":
		return "text-red-400"
	case "nvidia":
		return "text-green-400"
	case "apple":
		return "apple-gradient"
	default:
		return "text-white"
	}
}

func getVendorDisplay(vendor string) string {
	vendorLower := strings.ToLower(vendor)
	switch vendorLower {
	case "intel", "genuineintel":
		return "Intel"
	case "amd", "authenticamd":
		return "AMD"
	case "nvidia":
		return "NVIDIA"
	case "apple":
		return "Apple"
	default:
		return vendor
	}
}

func isX86Architecture(arch string) bool {
	arch = strings.ToLower(arch)
	return arch == "amd64" || arch == "x86_64" || arch == "386"
}

func isARMArchitecture(arch string) bool {
	arch = strings.ToLower(arch)
	return arch == "arm64" || arch == "aarch64" || arch == "arm"
}

// getDistroLogo returns logo filename for a Linux distribution
func getDistroLogo(distro string) string {
	distroLower := strings.ToLower(distro)

	// Map distro names to logo filenames
	switch {
	case distroLower == "ubuntu" || distroLower == "kubuntu" || strings.Contains(distroLower, "ubuntu"):
		return "ubuntu.avif"
	case distroLower == "debian":
		return "debian.avif"
	case distroLower == "fedora":
		return "fedora.avif"
	case distroLower == "arch":
		return "arch.avif"
	case distroLower == "cachyos":
		return "cachyos.svg"
	case distroLower == "opensuse" || distroLower == "suse":
		return "opensuse.avif"
	case distroLower == "centos":
		return "centos.svg"
	case distroLower == "redhat" || distroLower == "rhel":
		return "redhat.avif"
	case distroLower == "rocky":
		return "rocky.avif"
	case distroLower == "almalinux":
		return "almalinux.avif"
	case distroLower == "linuxmint" || distroLower == "mint":
		return "mint.avif"
	case distroLower == "pop" || distroLower == "popos" || distroLower == "pop!_os":
		return "popos.avif"
	case distroLower == "manjaro":
		return "manjaro.svg"
	case distroLower == "endeavouros" || distroLower == "endeavor":
		return "endeavor.avif"
	case distroLower == "garuda":
		return "garuda.avif"
	case distroLower == "gentoo":
		return "gentoo.avif"
	case distroLower == "nixos":
		return "nixos.avif"
	case distroLower == "void":
		return "void.avif"
	case distroLower == "slackware":
		return "slackware.avif"
	case distroLower == "alpine":
		return "alpine.avif"
	case distroLower == "clearlinux":
		return "clearlinux.avif"
	case distroLower == "oracle":
		return "oracle.avif"
	case distroLower == "artix":
		return "artix.avif"
	case distroLower == "steamos":
		return "steamos.avif"
	case distroLower == "zorin":
		return "zorin.avif"
	case distroLower == "bazzite":
		return "bazzite.avif"
	case distroLower == "gobolinux":
		return "gobolinux.avif"
	case distroLower == "kdeneon":
		return "kdeneon.avif"
	case distroLower == "ubuntustudio":
		return "ubuntustudio.avif"
	default:
		return "linux.avif"
	}
}

// getPackageTypeLogo returns the logo filename for a package type (e.g., Flatpak, portable)
// For package manager type, it returns an empty string to allow caller to use distro-specific logo
// For installed apps, returns platform-specific icons
func getPackageTypeLogo(pkgType string, p *platform.Platform) string {
	switch strings.ToLower(pkgType) {
	case "flatpak":
		return "flatpak.avif"
	case "portable":
		return "portable.avif"
	case "installed app":
		// Return platform-specific icon for installed apps
		if p != nil {
			if p.IsDarwin() {
				return "mac-app.avif"
			}
			if p.IsWindows() {
				return "win-app.avif"
			}
		}
		return "installed-app.avif" // fallback for Linux
	case "package manager":
		// On macOS, return brew icon since Homebrew is the package manager
		if p != nil && p.IsDarwin() {
			return "brew.avif"
		}
		// Return empty string - caller should use distro logo instead
		return ""
	default:
		return ""
	}
}

// ModalConfig represents configuration for confirmation modals
type ModalConfig struct {
	Title       string `json:"title"`
	Message     string `json:"message"`
	ConfirmText string `json:"confirm_text"`
	CancelText  string `json:"cancel_text"`
	ConfirmURL  string `json:"confirm_url"`
	ConfirmData string `json:"confirm_data"`
	Danger      bool   `json:"danger"`
	Success     bool   `json:"success"` // For success modals (green styling, no cancel button)

	// HTMX partial refresh configuration (optional, for partial page refresh)
	HTMXRefresh string `json:"htmx_refresh,omitempty"` // URL to fetch for refresh
	HTMXTarget  string `json:"htmx_target,omitempty"`  // ID of element to refresh
}

// toJSON converts a value to a JSON string for use in templates
func toJSON(v interface{}) string {
	b, err := json.Marshal(v)
	if err != nil {
		return "[]"
	}
	return string(b)
}

// toString converts any type to a string for use in templates
// This is needed for custom types like platform.OSType
func toString(v interface{}) string {
	switch val := v.(type) {
	case string:
		return val
	case int, int8, int16, int32, int64:
		return fmt.Sprintf("%d", val)
	case uint, uint8, uint16, uint32, uint64:
		return fmt.Sprintf("%d", val)
	case float32, float64:
		return fmt.Sprintf("%f", val)
	case bool:
		return fmt.Sprintf("%t", val)
	default:
		return fmt.Sprintf("%v", val)
	}
}

// capitalize capitalizes the first letter of a string
func capitalize(s string) string {
	if len(s) == 0 {
		return s
	}
	return strings.ToUpper(string(s[0])) + s[1:]
}

// capitalizeArchitecture formats architecture string with proper capitalization
func capitalizeArchitecture(arch string) string {
	archLower := strings.ToLower(arch)
	switch archLower {
	case "amd64", "x86_64":
		return "AMD64"
	case "arm64", "aarch64":
		return "ARM64"
	case "arm":
		return "ARM"
	case "386":
		return "x86"
	default:
		return strings.ToUpper(arch)
	}
}

// getOSLogo returns logo filename for OS type
func getOSLogo(osType string) string {
	osLower := strings.ToLower(osType)
	switch osLower {
	case "linux":
		return "linux.avif"
	case "windows":
		return "windows.avif"
	case "darwin", "macos":
		return "macos.avif"
	default:
		return "linux.avif" // fallback
	}
}

// getOSDisplayName returns the display name for OS type
func getOSDisplayName(osType string) string {
	osLower := strings.ToLower(osType)
	switch osLower {
	case "linux":
		return "Linux"
	case "windows":
		return "Windows"
	case "darwin":
		return "macOS"
	default:
		return strings.Title(osType) // fallback with title case
	}
}

// getDistroFamilyLogo returns logo filename for distro family
func getDistroFamilyLogo(family string) string {
	familyLower := strings.ToLower(family)
	switch familyLower {
	case "debian":
		return "debian.avif"
	case "rhel", "redhat", "red hat":
		return "redhat.avif"
	case "fedora":
		return "fedora.avif"
	case "arch":
		return "arch.avif"
	case "opensuse", "suse":
		return "opensuse.avif"
	case "ubuntu":
		return "ubuntu.avif"
	case "centos":
		return "centos.avif" // if we have it, otherwise fallback
	case "rocky":
		return "rocky.avif" // if we have it, otherwise fallback
	case "almalinux":
		return "almalinux.avif"
	case "manjaro":
		return "manjaro.avif" // if we have it, otherwise fallback
	case "endeavouros":
		return "endeavouros.avif" // if we have it, otherwise fallback
	case "nixos":
		return "nixos.avif" // if we have it, otherwise fallback
	case "void":
		return "void.avif" // if we have it, otherwise fallback
	default:
		// Try to find a matching distro logo
		return getDistroLogo(family)
	}
}

// MajorLanguageData holds data for major language display
type MajorLanguageData struct {
	LanguageCode  string `json:"language_code"`
	LanguageName  string `json:"language_name"`
	LanguageLocal string `json:"language_local"`
	Flag          string `json:"flag"`
}

// RegionalVariantData holds data for regional variant display
type RegionalVariantData struct {
	Locale               string `json:"locale"`
	CountryName          string `json:"country_name"`
	CountryLocal         string `json:"country_local"`
	CountryCode          string `json:"country_code"`
	Flag                 string `json:"flag"`
	MajorRegionalVariant bool   `json:"major_regional_variant"`
	Population           int    `json:"population"`
}

// ExtendedRegionalData holds regional data with parent language info for search
type ExtendedRegionalData struct {
	Locale               string `json:"locale"`
	CountryName          string `json:"country_name"`
	CountryLocal         string `json:"country_local"`
	CountryCode          string `json:"country_code"`
	Flag                 string `json:"flag"`
	MajorRegionalVariant bool   `json:"major_regional_variant"`
	Population           int    `json:"population"`
	LanguageCode         string `json:"language_code"`
	LanguageName         string `json:"language_name"`
	LanguageLocal        string `json:"language_local"`
}

// LanguagePriorityData holds data for language priority list display
type LanguagePriorityData struct {
	Index         int    `json:"index"`
	LanguageCode  string `json:"language_code"`
	LanguageName  string `json:"language_name"`
	LanguageLocal string `json:"language_local"`
	Flag          string `json:"flag"`
	Locale        string `json:"locale"`
}

// LanguagesPageData holds data for the language preferences page
type LanguagesPageData struct {
	Title                 string
	Nav                   []NavItem
	Config                *ConfigSettings
	AppVersion            string
	MajorLanguages        []MajorLanguageData
	SelectedMajorLanguage *MajorLanguageData
	RegionalVariants      []RegionalVariantData
	AudioPriority         []LanguagePriorityData
	SubtitlePriority      []LanguagePriorityData
}

// HotkeyCategoryData holds hotkeys grouped by category
type HotkeyCategoryData struct {
	Category string
	Hotkeys  []HotkeyItem
	Count    int
}

// HotkeyItem represents a single hotkey for display
type HotkeyItem struct {
	ID          string
	Keys        string
	Description string
	Category    string
	Priority    int
}

// HotkeysPageData holds data for the keyboard shortcuts page
type HotkeysPageData struct {
	Title          string
	Nav            []NavItem
	AppVersion     string
	Categories     []HotkeyCategoryData
	CategoryCounts []CategoryCountItem
	TotalCount     int
}

// CategoryCountItem holds category name and count for filter buttons
type CategoryCountItem struct {
	Category string
	Count    int
}

// getHWABadgeColor returns CSS class for hardware acceleration status badge
func getHWABadgeColor(hwa string) string {
	hwaLower := strings.ToLower(hwa)
	if hwaLower == "no" || hwaLower == "" {
		return "bg-red-600" // Disabled
	}
	return "bg-green-600" // Enabled
}

// getHWABadgeText returns "Enabled" or "Disabled" text for badge
func getHWABadgeText(hwa string) string {
	hwaLower := strings.ToLower(hwa)
	if hwaLower == "no" || hwaLower == "" {
		return "Disabled"
	}
	return "Enabled"
}

// getGPUAPIDescription returns human-readable description for GPU API
func getGPUAPIDescription(gpuAPI string) string {
	gpuAPILower := strings.ToLower(gpuAPI)
	switch gpuAPILower {
	case "auto":
		return "Automatic selection"
	case "opengl":
		return "OpenGL"
	case "vulkan":
		return "Vulkan"
	case "d3d11":
		return "Direct3D 11 (Windows)"
	case "d3d9":
		return "Direct3D 9 (Windows)"
	case "d3d9ex":
		return "Direct3D 9Ex (Windows)"
	case "dxinterop":
		return "DirectX Interop (Windows)"
	case "d3d11-interop":
		return "Direct3D 11 Interop (Windows)"
	case "metal":
		return "Metal (macOS)"
	case "":
		return "Not configured"
	default:
		return gpuAPI
	}
}

// categoryIcon returns a Boxicon class for a hotkey category
func categoryIcon(category string) string {
	switch category {
	case "Playback":
		return "bx-play-circle"
	case "Seeking":
		return "bx-search-alt"
	case "Audio & Volume":
		return "bx-volume-full"
	case "Subtitles":
		return "bx-comment-detail"
	case "Video & Display":
		return "bx-desktop"
	case "Playlist":
		return "bx-list-ul"
	case "Screenshots":
		return "bx-camera"
	case "Advanced":
		return "bx-slider-alt"
	case "Menu Navigation":
		return "bx-menu"
	case "Multimedia Keys":
		return "bx-keyboard"
	default:
		return "bx-key"
	}
}

// formatKeys formats a key string into styled keyboard keys HTML
func formatKeys(keys string) template.HTML {
	// Split by common separators
	parts := strings.Fields(keys)
	var html strings.Builder

	for i, part := range parts {
		// Check for separator tokens: / (or), + (connector), "or"
		// Only treat as separator if NOT at the last position
		// (if + or / is at the end, it's the actual key being pressed)
		if (part == "/" || part == "+" || part == "or") && i < len(parts)-1 {
			if part == "+" {
				html.WriteString(`<span class="text-gray-500 mx-0.5">+</span>`)
			} else {
				html.WriteString(`<span class="kbd-separator mx-1">` + part + `</span>`)
			}
			continue
		}

		// Check if this is a single uppercase letter (requires Shift)
		if len(part) == 1 && part[0] >= 'A' && part[0] <= 'Z' {
			// Convert to "Shift + lowercase" format
			lowerKey := strings.ToLower(part)
			html.WriteString(`<span class="kbd">Shift</span>`)
			html.WriteString(`<span class="text-gray-500 mx-0.5">+</span>`)
			html.WriteString(`<span class="kbd">` + lowerKey + `</span>`)
		} else if strings.Contains(part, "+") && len(part) > 1 {
			// Handle combined keys like "Ctrl+Shift+→" (but not standalone "+")
			subParts := strings.Split(part, "+")
			for j, subPart := range subParts {
				// Check if subPart is a single uppercase letter
				if len(subPart) == 1 && subPart[0] >= 'A' && subPart[0] <= 'Z' {
					// Already has modifiers, just convert to lowercase
					subPart = strings.ToLower(subPart)
				}
				if j > 0 {
					html.WriteString(`<span class="text-gray-500 mx-0.5">+</span>`)
				}
				html.WriteString(`<span class="kbd">` + formatSingleKey(subPart) + `</span>`)
			}
		} else {
			html.WriteString(`<span class="kbd">` + formatSingleKey(part) + `</span>`)
		}
	}

	return template.HTML(html.String())
}

// formatSingleKey formats a single key name for display
func formatSingleKey(key string) string {
	// Convert arrow symbols to text or keep as is
	key = strings.TrimSpace(key)
	switch key {
	case "→":
		return "→"
	case "←":
		return "←"
	case "↑":
		return "↑"
	case "↓":
		return "↓"
	case "⌘":
		return "⌘"
	case "+":
		return "+" // Plus key
	case "-":
		return "-" // Minus key
	case "/":
		return "/" // Slash key
	case "*":
		return "*" // Asterisk key
	default:
		return key
	}
}
