package updater

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"
)

const (
	// DefaultGitHubRepo is the default repository for agnt
	DefaultGitHubRepo = "standardbeagle/agnt"

	// GitHubAPIURL is the base URL for GitHub API
	GitHubAPIURL = "https://api.github.com"
)

// GitHubRelease represents a GitHub release
type GitHubRelease struct {
	TagName     string    `json:"tag_name"`
	Name        string    `json:"name"`
	Draft       bool      `json:"draft"`
	Prerelease  bool      `json:"prerelease"`
	PublishedAt time.Time `json:"published_at"`
	HTMLURL     string    `json:"html_url"`
	Body        string    `json:"body"`
}

// GitHubChecker checks for updates from GitHub releases
type GitHubChecker struct {
	repo       string
	baseURL    string
	httpClient *http.Client
}

// NewGitHubChecker creates a new GitHub release checker
func NewGitHubChecker(repo string) *GitHubChecker {
	if repo == "" {
		repo = DefaultGitHubRepo
	}

	return &GitHubChecker{
		repo:    repo,
		baseURL: GitHubAPIURL,
		httpClient: &http.Client{
			Timeout: 10 * time.Second,
		},
	}
}

// CheckLatestRelease fetches the latest release from GitHub
func (g *GitHubChecker) CheckLatestRelease() (*GitHubRelease, error) {
	base := g.baseURL
	if base == "" {
		base = GitHubAPIURL
	}
	url := fmt.Sprintf("%s/repos/%s/releases/latest", base, g.repo)

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	// Set User-Agent header (GitHub API requires it)
	req.Header.Set("User-Agent", "agnt-updater")
	req.Header.Set("Accept", "application/vnd.github+json")

	resp, err := g.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to fetch release: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("github API returned status %d: %s", resp.StatusCode, string(body))
	}

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

	return &release, nil
}

// GetVersion extracts version from release tag
// Handles tags like "v0.6.5", "V0.6.5", or "0.6.5"
func (g *GitHubRelease) GetVersion() string {
	version := g.TagName
	if strings.HasPrefix(strings.ToLower(version), "v") {
		return version[1:]
	}
	return version
}

// IsNewer checks if this release is newer than the given version
func (g *GitHubRelease) IsNewer(currentVersion string) (bool, error) {
	releaseVersion := g.GetVersion()
	currentVersion = strings.TrimPrefix(currentVersion, "v")

	// Parse versions
	relMajor, relMinor, relPatch, err := parseVersion(releaseVersion)
	if err != nil {
		return false, fmt.Errorf("failed to parse release version %s: %w", releaseVersion, err)
	}

	curMajor, curMinor, curPatch, err := parseVersion(currentVersion)
	if err != nil {
		return false, fmt.Errorf("failed to parse current version %s: %w", currentVersion, err)
	}

	// Compare versions
	if relMajor > curMajor {
		return true, nil
	}
	if relMajor < curMajor {
		return false, nil
	}

	if relMinor > curMinor {
		return true, nil
	}
	if relMinor < curMinor {
		return false, nil
	}

	if relPatch > curPatch {
		return true, nil
	}

	return false, nil
}

// parseVersion parses a semantic version string
func parseVersion(version string) (major, minor, patch int, err error) {
	version = strings.TrimPrefix(version, "v")

	// Remove pre-release and build metadata
	if idx := strings.IndexAny(version, "-+"); idx > 0 {
		version = version[:idx]
	}

	n, err := fmt.Sscanf(version, "%d.%d.%d", &major, &minor, &patch)
	if err != nil || n != 3 {
		return 0, 0, 0, fmt.Errorf("invalid version format: %s", version)
	}

	return major, minor, patch, nil
}
