package runner

import (
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
)

// InstallFont downloads a font file and installs it into the OS so After Effects
// can use it. Returns nil on success. Best-effort per platform.
func InstallFont(ctx context.Context, fileURL, name string) error {
	ext := filepath.Ext(strings.Split(fileURL, "?")[0])
	if ext == "" {
		ext = ".ttf"
	}
	fileName := sanitizeFontName(name) + ext

	tmpDir, err := os.MkdirTemp("", "fr-font-")
	if err != nil {
		return fmt.Errorf("tmp dir: %w", err)
	}
	defer os.RemoveAll(tmpDir)
	tmp := filepath.Join(tmpDir, fileName)
	if _, err := DownloadFile(ctx, fileURL, tmp); err != nil {
		return fmt.Errorf("download: %w", err)
	}

	switch runtime.GOOS {
	case "windows":
		return installFontWindows(ctx, tmp, fileName)
	case "darwin":
		return copyFile(tmp, filepath.Join(os.Getenv("HOME"), "Library", "Fonts", fileName))
	default: // linux
		dir := filepath.Join(os.Getenv("HOME"), ".local", "share", "fonts")
		_ = os.MkdirAll(dir, 0o755)
		if err := copyFile(tmp, filepath.Join(dir, fileName)); err != nil {
			return err
		}
		_ = exec.CommandContext(ctx, "fc-cache", "-f").Run()
		return nil
	}
}

func installFontWindows(ctx context.Context, src, fileName string) error {
	// Prefer the machine-wide Fonts dir; fall back to the per-user dir.
	dst := filepath.Join(os.Getenv("WINDIR"), "Fonts", fileName)
	if err := copyFile(src, dst); err != nil {
		userFonts := filepath.Join(os.Getenv("LOCALAPPDATA"), "Microsoft", "Windows", "Fonts")
		_ = os.MkdirAll(userFonts, 0o755)
		dst = filepath.Join(userFonts, fileName)
		if err := copyFile(src, dst); err != nil {
			return fmt.Errorf("copy font: %w", err)
		}
	}
	// Register so Windows/AE pick it up without a reboot (best-effort; needs admin).
	_ = exec.CommandContext(ctx, "reg", "add",
		`HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts`,
		"/v", fileName, "/t", "REG_SZ", "/d", dst, "/f").Run()
	return nil
}

func copyFile(src, dst string) error {
	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()
	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()
	if _, err := io.Copy(out, in); err != nil {
		return err
	}
	return out.Sync()
}

func sanitizeFontName(s string) string {
	s = strings.TrimSpace(s)
	repl := strings.NewReplacer("/", "_", "\\", "_", ":", "_", "*", "_", "?", "_", "\"", "_", "<", "_", ">", "_", "|", "_", " ", "_")
	s = repl.Replace(s)
	if s == "" {
		return "font"
	}
	return s
}
