package web

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"
)

// VersionChecker periodically queries the Gitea Docker Registry V2 API
// for the latest controller image version tag.
type VersionChecker struct {
	image         string // e.g., "gitea.dooplex.hu/admin/felhom-controller"
	username      string
	token         string
	checkInterval time.Duration
	logger        *log.Logger

	mu            sync.RWMutex
	latestVersion string
	lastCheck     time.Time
	lastError     string
}

// NewVersionChecker creates a new VersionChecker.
func NewVersionChecker(image, username, token string, checkInterval time.Duration, logger *log.Logger) *VersionChecker {
	return &VersionChecker{
		image:         image,
		username:      username,
		token:         token,
		checkInterval: checkInterval,
		logger:        logger,
	}
}

// Run starts the periodic version check loop. Call in a goroutine.
// It checks immediately on start, then every checkInterval.
func (vc *VersionChecker) Run(ctx context.Context) {
	vc.check()
	ticker := time.NewTicker(vc.checkInterval)
	defer ticker.Stop()
	for {
		select {
		case <-ctx.Done():
			return
		case <-ticker.C:
			vc.check()
		}
	}
}

func (vc *VersionChecker) check() {
	latest, err := vc.queryRegistry()
	vc.mu.Lock()
	defer vc.mu.Unlock()
	vc.lastCheck = time.Now()
	if err != nil {
		vc.lastError = err.Error()
		vc.logger.Printf("[WARN] Registry version check failed: %v", err)
		return
	}
	vc.lastError = ""
	vc.latestVersion = latest
	vc.logger.Printf("[DEBUG] Registry version check: latest = %s", latest)
}

// LatestVersion returns the cached latest version string (e.g., "0.16.1"), or "" if unknown.
func (vc *VersionChecker) LatestVersion() string {
	vc.mu.RLock()
	defer vc.mu.RUnlock()
	return vc.latestVersion
}

// queryRegistry queries the Gitea Docker Registry V2 API for available tags
// and returns the highest valid semver tag.
func (vc *VersionChecker) queryRegistry() (string, error) {
	// Extract "owner/repo" from image reference
	// e.g., "gitea.dooplex.hu/admin/felhom-controller" → "admin/felhom-controller"
	imagePath := vc.image
	if parts := strings.SplitN(vc.image, "/", 2); len(parts) == 2 {
		imagePath = parts[1]
	}

	url := fmt.Sprintf("https://gitea.dooplex.hu/v2/%s/tags/list", imagePath)

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return "", fmt.Errorf("creating request: %w", err)
	}
	req.SetBasicAuth(vc.username, vc.token)

	client := &http.Client{Timeout: 15 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", fmt.Errorf("HTTP request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode == 401 {
		return "", fmt.Errorf("authentication failed (401)")
	}
	if resp.StatusCode != 200 {
		return "", fmt.Errorf("unexpected status: %d", resp.StatusCode)
	}

	var tagsResp struct {
		Tags []string `json:"tags"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&tagsResp); err != nil {
		return "", fmt.Errorf("decoding response: %w", err)
	}

	// Find the highest valid semver tag
	var bestMajor, bestMinor, bestPatch int
	found := false
	for _, tag := range tagsResp.Tags {
		tag = strings.TrimPrefix(tag, "v")
		parts := strings.SplitN(tag, ".", 3)
		if len(parts) != 3 {
			continue
		}
		major, e1 := strconv.Atoi(parts[0])
		minor, e2 := strconv.Atoi(parts[1])
		patch, e3 := strconv.Atoi(parts[2])
		if e1 != nil || e2 != nil || e3 != nil {
			continue
		}
		if !found || major > bestMajor ||
			(major == bestMajor && minor > bestMinor) ||
			(major == bestMajor && minor == bestMinor && patch > bestPatch) {
			bestMajor, bestMinor, bestPatch = major, minor, patch
			found = true
		}
	}

	if !found {
		return "", fmt.Errorf("no valid semver tags found")
	}

	return fmt.Sprintf("%d.%d.%d", bestMajor, bestMinor, bestPatch), nil
}
