//go:build linux

package utils

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"git.leaktechnologies.dev/stu/VideoTools/internal/logging"
)

// EnsureLinuxDesktopEntry installs a user-level desktop entry and icon so GNOME can
// associate the running app with a stable icon.
func EnsureLinuxDesktopEntry(appID, appName string) {
	iconPath := findLinuxIconPath()
	if iconPath == "" {
		logging.Debug(logging.CatUI, "desktop entry skipped: icon not found")
		return
	}

	exe, err := os.Executable()
	if err != nil || exe == "" {
		logging.Debug(logging.CatUI, "desktop entry skipped: executable path unavailable")
		return
	}

	home, err := os.UserHomeDir()
	if err != nil || home == "" {
		logging.Debug(logging.CatUI, "desktop entry skipped: home dir unavailable")
		return
	}

	iconDir := filepath.Join(home, ".local", "share", "icons", "hicolor", "256x256", "apps")
	if err := os.MkdirAll(iconDir, 0755); err != nil {
		logging.Debug(logging.CatUI, "desktop entry skipped: create icon dir failed: %v", err)
		return
	}
	iconTarget := filepath.Join(iconDir, fmt.Sprintf("%s.png", appID))
	if err := copyFileIfDifferent(iconPath, iconTarget); err != nil {
		logging.Debug(logging.CatUI, "desktop entry skipped: icon copy failed: %v", err)
		return
	}

	desktopDir := filepath.Join(home, ".local", "share", "applications")
	if err := os.MkdirAll(desktopDir, 0755); err != nil {
		logging.Debug(logging.CatUI, "desktop entry skipped: create desktop dir failed: %v", err)
		return
	}
	desktopTarget := filepath.Join(desktopDir, fmt.Sprintf("%s.desktop", appID))
	desktopContents := fmt.Sprintf(`[Desktop Entry]
Name=%s
Exec=%s
Icon=%s
Type=Application
Categories=AudioVideo;Video;Utility;
Terminal=false
StartupWMClass=%s
`, appName, exe, appID, appID)

	if err := writeFileIfDifferent(desktopTarget, desktopContents); err != nil {
		logging.Debug(logging.CatUI, "desktop entry skipped: write failed: %v", err)
		return
	}
}

func findLinuxIconPath() string {
	candidates := []string{
		filepath.Join("assets", "logo", "VT_Icon.png"),
	}
	if exe, err := os.Executable(); err == nil {
		dir := filepath.Dir(exe)
		candidates = append(candidates, filepath.Join(dir, "assets", "logo", "VT_Icon.png"))
	}

	for _, p := range candidates {
		if _, err := os.Stat(p); err == nil {
			return p
		}
	}
	return ""
}

func copyFileIfDifferent(src, dst string) error {
	srcInfo, err := os.Stat(src)
	if err != nil {
		return err
	}
	if dstInfo, err := os.Stat(dst); err == nil {
		if srcInfo.Size() == dstInfo.Size() && srcInfo.ModTime().Equal(dstInfo.ModTime()) {
			return nil
		}
	}

	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()

	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	if _, err := io.Copy(out, in); err != nil {
		out.Close()
		return err
	}
	if err := out.Close(); err != nil {
		return err
	}
	return os.Chtimes(dst, srcInfo.ModTime(), srcInfo.ModTime())
}

func writeFileIfDifferent(path, contents string) error {
	if existing, err := os.ReadFile(path); err == nil {
		if strings.TrimSpace(string(existing)) == strings.TrimSpace(contents) {
			return nil
		}
	}
	return os.WriteFile(path, []byte(contents), 0644)
}
