package updater

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os/exec"
	"regexp"
	"strings"
	"time"

	"github.com/muyue/muyue/internal/scanner"
)

type UpdateStatus struct {
	Tool        string `json:"tool"`
	Current     string `json:"current"`
	Latest      string `json:"latest"`
	NeedsUpdate bool   `json:"needs_update"`
	Message     string `json:"message,omitempty"`
	Error       string `json:"error,omitempty"`
}

var versionRegex = regexp.MustCompile(`\d+\.\d+\.\d+`)

var sharedHTTPClient = &http.Client{Timeout: 10 * time.Second}

type githubRelease struct {
	TagName string `json:"tag_name"`
}

func CheckUpdates(result *scanner.ScanResult) []UpdateStatus {
	var statuses []UpdateStatus

	for _, tool := range result.Tools {
		if !tool.Installed {
			continue
		}

		status := UpdateStatus{
			Tool:    tool.Name,
			Current: versionRegex.FindString(tool.Version),
		}

		latest, err := getLatestVersion(tool.Name)
		if err != nil {
			status.Error = err.Error()
			statuses = append(statuses, status)
			continue
		}

		status.Latest = latest
		status.NeedsUpdate = status.Current != "" && status.Current != latest
		statuses = append(statuses, status)
	}

	return statuses
}

func getLatestVersion(tool string) (string, error) {
	repos := map[string]string{
		"crush":     "charmbracelet/crush",
		"gh":        "cli/cli",
		"starship":  "starship/starship",
		"docker":    "moby/moby",
	}

	repo, ok := repos[tool]
	if !ok {
		return getLatestVersionCLI(tool)
	}

	url := fmt.Sprintf("https://api.github.com/repos/%s/releases/latest", repo)

	resp, err := sharedHTTPClient.Get(url)
	if err != nil {
		return "", fmt.Errorf("github api: %w", err)
	}
	defer resp.Body.Close()

	var release githubRelease
	if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
		return "", fmt.Errorf("decode: %w", err)
	}

	return strings.TrimPrefix(release.TagName, "v"), nil
}

func getLatestVersionCLI(tool string) (string, error) {
	commands := map[string][]string{
		"node":    {"node", "--version"},
		"npm":     {"npm", "--version"},
		"go":      {"go", "version"},
		"python3": {"python3", "--version"},
		"git":     {"git", "--version"},
		"claude":  {"claude", "--version"},
	}

	cmd, ok := commands[tool]
	if !ok {
		return "", fmt.Errorf("no update check for %s", tool)
	}

	out, err := exec.Command(cmd[0], cmd[1:]...).Output()
	if err != nil {
		return "", err
	}

	v := versionRegex.FindString(string(out))
	if v == "" {
		return strings.TrimSpace(string(out)), nil
	}
	return v, nil
}

func RunAutoUpdate(statuses []UpdateStatus) []UpdateStatus {
	var results []UpdateStatus
	for _, s := range statuses {
		if !s.NeedsUpdate || s.Error != "" {
			results = append(results, s)
			continue
		}

		switch s.Tool {
		case "crush":
			err := runCommand("bash", "-c",
				"curl -fsSL https://github.com/charmbracelet/crush/releases/latest/download/install.sh | bash")
			if err != nil {
				s.Error = err.Error()
			} else {
				s.NeedsUpdate = false
				s.Message = "updated"
			}
		case "claude":
			err := runCommand("npm", "update", "-g", "@anthropic-ai/claude-code")
			if err != nil {
				s.Error = err.Error()
			} else {
				s.NeedsUpdate = false
				s.Message = "updated"
			}
		case "starship":
			err := runCommand("bash", "-c",
				"curl -sS https://starship.rs/install.sh | sh -s -- -y")
			if err != nil {
				s.Error = err.Error()
			} else {
				s.NeedsUpdate = false
				s.Message = "updated"
			}
		default:
			s.Error = "auto-update not supported"
		}

		results = append(results, s)
	}
	return results
}

func runCommand(name string, args ...string) error {
	cmd := exec.Command(name, args...)
	return cmd.Run()
}
