// cmd/genicon rasterizes branded SVGs into PNG assets and generates native
// icon files (.icns and .ico) for Electron packaging.
//
// Usage:
//
//	go run ./cmd/genicon              # generate PNGs only (requires rsvg-convert)
//	go run ./cmd/genicon -native      # also generate .icns and .ico
package main

import (
	"bytes"
	"flag"
	"fmt"
	"image/png"
	"log/slog"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/jackmordaunt/icns/v2"
	"github.com/leaanthony/winicon"

	"github.com/tidbcloud/AgentTeam/desktop/internal/icon"
)

var (
	flagNative = flag.Bool("native", false, "generate native icon files (.icns and .ico)")
)

func main() {
	flag.Parse()

	iconDir := filepath.Join("internal", "icon", "assets")
	buildDir := filepath.Join("electron", "build")
	mustMkdirAll(iconDir)

	mustRasterize(filepath.Join(iconDir, "app-white.svg"), filepath.Join(iconDir, "icon-white.png"))
	mustRasterize(filepath.Join(iconDir, "app-black.svg"), filepath.Join(iconDir, "icon-black.png"))
	mustRasterize(filepath.Join(iconDir, "tray-white.svg"), filepath.Join(iconDir, "tray-white.png"))
	mustRasterize(filepath.Join(iconDir, "tray-black.svg"), filepath.Join(iconDir, "tray-black.png"))

	// Produce a padded dock-ready PNG alongside the raw icon-black.png. The
	// ~10% transparent margin + rounded corners matches Apple HIG so the icon
	// does not look oversized in the macOS Dock or Windows taskbar.
	iconBlackPath := filepath.Join(iconDir, "icon-black.png")
	iconBlackBytes, err := os.ReadFile(iconBlackPath)
	if err != nil {
		fatalf("read %s: %v", iconBlackPath, err)
	}
	dockPNG, err := icon.RoundPNGCorners(iconBlackBytes)
	if err != nil {
		fatalf("round dock PNG: %v", err)
	}
	dockPath := filepath.Join(iconDir, "icon-dock.png")
	if err := os.WriteFile(dockPath, dockPNG, 0o644); err != nil {
		fatalf("write %s: %v", dockPath, err)
	}
	slog.Info("generated dock icon", "path", dockPath)

	if !*flagNative {
		return
	}

	mustMkdirAll(buildDir)
	iconPNG := iconBlackPath

	icnsPath := filepath.Join(buildDir, "icon.icns")
	if err := generateICNS(iconPNG, icnsPath); err != nil {
		fatalf("generate .icns: %v", err)
	}
	slog.Info("generated", "path", icnsPath)

	icoPath := filepath.Join(buildDir, "icon.ico")
	if err := generateICO(iconPNG, icoPath); err != nil {
		fatalf("generate .ico: %v", err)
	}
	slog.Info("generated", "path", icoPath)

}

func mustMkdirAll(dir string) {
	if err := os.MkdirAll(dir, 0755); err != nil {
		fatalf("mkdir %s: %v", dir, err)
	}
}

func mustRasterize(src, dst string) {
	if err := rasterize(src, dst); err != nil {
		fatalf("rasterize %s: %v", src, err)
	}
}

func rasterize(src, dst string) error {
	rsvgConvert, err := exec.LookPath("rsvg-convert")
	if err != nil {
		return fmt.Errorf("rsvg-convert not found in PATH: %w", err)
	}
	cmd := exec.Command(rsvgConvert, "-w", "256", "-h", "256", src, "-o", dst)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

func generateICNS(pngPath, outPath string) error {
	iconData, err := os.ReadFile(pngPath)
	if err != nil {
		return fmt.Errorf("read png: %w", err)
	}
	srcImg, err := png.Decode(bytes.NewReader(iconData))
	if err != nil {
		return fmt.Errorf("decode png: %w", err)
	}
	dest, err := os.Create(outPath)
	if err != nil {
		return fmt.Errorf("create output: %w", err)
	}
	defer dest.Close()
	return icns.Encode(dest, srcImg)
}

func generateICO(pngPath, outPath string) error {
	iconData, err := os.ReadFile(pngPath)
	if err != nil {
		return fmt.Errorf("read png: %w", err)
	}
	var buf bytes.Buffer
	if err := winicon.GenerateIcon(bytes.NewReader(iconData), &buf, []int{256, 128, 64, 48, 32, 16}); err != nil {
		return fmt.Errorf("generate icon: %w", err)
	}
	return os.WriteFile(outPath, buf.Bytes(), 0644)
}

func fatalf(format string, args ...any) {
	fmt.Fprintf(os.Stderr, format+"\n", args...)
	os.Exit(1)
}
