package update

import (
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"

	"vrc_osc_go/internal/buildinfo"
	"vrc_osc_go/internal/common"
)

type Manager struct {
	Client       *Client
	OwnerRepo    string
	AssetName    string
	CurrentLabel string
}

func (m *Manager) CheckAndUpdate() (bool, error) {
	if m.Client == nil || m.OwnerRepo == "" || m.AssetName == "" {
		return false, nil
	}
	latest, err := m.Client.LatestRelease(m.OwnerRepo)
	if err != nil {
		return false, err
	}
	if latest.TagName == "" || latest.TagName == m.CurrentLabel {
		return false, nil
	}
	assetURL := ""
	for _, a := range latest.Assets {
		if a.Name == m.AssetName {
			assetURL = a.URL
			break
		}
	}
	if assetURL == "" {
		return false, fmt.Errorf("asset %q not found in release %s", m.AssetName, latest.TagName)
	}
	base := common.RootDir()
	tmpDir := filepath.Join(base, "update", latest.TagName)
	if err := os.MkdirAll(tmpDir, 0o755); err != nil {
		return false, err
	}
	zipPath := filepath.Join(tmpDir, m.AssetName)
	log.Printf("update available: %s -> %s", m.CurrentLabel, latest.TagName)
	if err := m.Client.DownloadAsset(assetURL, zipPath); err != nil {
		return false, err
	}
	if err := ExtractZip(zipPath, tmpDir); err != nil {
		return false, err
	}
	exePath := filepath.Join(base, CurrentExeName())
	newExe := filepath.Join(tmpDir, CurrentExeName())
	if _, err := os.Stat(newExe); err != nil {
		return false, err
	}
	backup := exePath + ".bak"
	_ = os.Remove(backup)
	if err := os.Rename(exePath, backup); err != nil {
		return false, err
	}
	if err := CopyFile(newExe, exePath); err != nil {
		_ = os.Rename(backup, exePath)
		return false, err
	}
	_ = os.Remove(backup)
	return true, nil
}

func CopyFile(src, dst string) error {
	in, err := os.Open(src)
	if err != nil {
		return err
	}
	defer in.Close()
	if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
		return err
	}
	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer out.Close()
	_, err = io.Copy(out, in)
	return err
}

func Launch(exe string, args []string) error {
	cmd := exec.Command(exe, args...)
	cmd.Dir = filepath.Dir(exe)
	return cmd.Start()
}

func Version() string { return buildinfo.Version }

func CurrentExeName() string {
	if runtime.GOOS == "windows" {
		return "vrc_osc.exe"
	}
	return "vrc_osc"
}
