package main

import (
	"encoding/json"
	"net/http"
	"strconv"
	"strings"
	"time"

	"hamlog/internal/applog"
)

// updateCheckURL is the GitHub Releases "latest" endpoint for the public OpsLog
// build (the exe lives there; source stays on Gitea). Adjust the repo if needed.
const updateCheckURL = "https://api.github.com/repos/GregTroar/OpsLog/releases/latest"

// UpdateInfo is the result of the startup version check.
type UpdateInfo struct {
	Current   string `json:"current"`   // this build's version (appVersion)
	Latest    string `json:"latest"`    // newest published release, "" if unknown
	Available bool   `json:"available"` // Latest > Current
	URL       string `json:"url"`       // release page to open
}

// CheckForUpdate asks GitHub for the latest release and compares it to this
// build. Best effort — on any failure it reports "no update" so the app never
// nags about a check it couldn't complete.
func (a *App) CheckForUpdate() UpdateInfo {
	out := UpdateInfo{Current: appVersion}
	client := &http.Client{Timeout: 8 * time.Second}
	req, err := http.NewRequest(http.MethodGet, updateCheckURL, nil)
	if err != nil {
		return out
	}
	req.Header.Set("Accept", "application/vnd.github+json")
	resp, err := client.Do(req)
	if err != nil {
		applog.Printf("update: check failed: %v", err)
		return out
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return out // no release yet (404) or rate-limited — treat as up to date
	}
	var r struct {
		TagName string `json:"tag_name"`
		HTMLURL string `json:"html_url"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
		return out
	}
	out.Latest = strings.TrimPrefix(strings.TrimSpace(r.TagName), "v")
	out.URL = r.HTMLURL
	out.Available = versionLess(appVersion, out.Latest)
	if out.Available {
		applog.Printf("update: newer version available — current=%s latest=%s", appVersion, out.Latest)
	}
	return out
}

// versionLess reports whether version a is older than b. Compares dot-separated
// numeric parts ("0.9" < "0.10" < "1.0"); non-numeric junk in a part counts as 0.
func versionLess(a, b string) bool {
	pa := strings.Split(a, ".")
	pb := strings.Split(b, ".")
	n := len(pa)
	if len(pb) > n {
		n = len(pb)
	}
	for i := 0; i < n; i++ {
		ai, bi := 0, 0
		if i < len(pa) {
			ai = leadingInt(pa[i])
		}
		if i < len(pb) {
			bi = leadingInt(pb[i])
		}
		if ai != bi {
			return ai < bi
		}
	}
	return false
}

// leadingInt parses the leading digits of s (e.g. "2beta" → 2), 0 if none.
func leadingInt(s string) int {
	s = strings.TrimSpace(s)
	end := 0
	for end < len(s) && s[end] >= '0' && s[end] <= '9' {
		end++
	}
	if end == 0 {
		return 0
	}
	n, _ := strconv.Atoi(s[:end])
	return n
}
