package system

import (
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/AgentTeam/desktop/internal/profile"
	"github.com/tidbcloud/AgentTeam/desktop/internal/upgrade"
	"github.com/tidbcloud/AgentTeam/desktop/internal/version"
)

// ---------------------------------------------------------------------------
// GET /system-info
// ---------------------------------------------------------------------------

// SystemInfoHandler returns version, build, and runtime information.
type SystemInfoHandler struct{}

// NewSystemInfoHandler creates a new SystemInfoHandler.
func NewSystemInfoHandler() gin.HandlerFunc {
	return (&SystemInfoHandler{}).Handle
}

// SystemInfoResponse is the JSON response for GET /system-info.
type SystemInfoResponse struct {
	Version   string `json:"version"`
	Commit    string `json:"commit"`
	BuildAt   string `json:"buildAt"`
	GoVersion string `json:"goVersion"`
	OS        string `json:"os"`
	Arch      string `json:"arch"`
	PID       int    `json:"pid"`
	// StartTime is the process start time as ISO 8601 UTC.
	StartTime string `json:"startTime"`
	// Uptime is the duration since process start in seconds.
	Uptime               float64 `json:"uptime"`
	BinaryPath           string  `json:"binaryPath,omitempty"`
	BinaryDirWritable    bool    `json:"binaryDirWritable"`
	ConfigPath           string  `json:"configPath,omitempty"`
	ConfigDir            string  `json:"configDir,omitempty"`
	UpgradeLogPath       string  `json:"upgradeLogPath,omitempty"`
	UpgradeCommand       string  `json:"upgradeCommand,omitempty"`
	UpgradeCommandTarget string  `json:"upgradeCommandTarget,omitempty"`
	Hostname             string  `json:"hostname,omitempty"`
}

// processStartTime is captured once at package init.
var processStartTime = time.Now()

// Handle returns system information including version and uptime.
func (h *SystemInfoHandler) Handle(c *gin.Context) {
	c.JSON(http.StatusOK, BuildSystemInfo())
}

// BuildSystemInfo collects process/version/runtime data for the
// /system-info endpoint. It is exported so aggregate (bundle) endpoints
// can embed the same payload without re-implementing the logic.
func BuildSystemInfo() SystemInfoResponse {
	binaryPath, binaryDirWritable := resolveBinaryPathInfo()
	configDir := profile.ConfigDir()
	configPath := profile.Path()
	var upgradeLogPath, upgradeCommand, upgradeCommandTarget string
	if upgrade.SupportsHeadlessUpgrade(runtime.GOOS) {
		upgradeLogPath = filepath.Join(configDir, "upgrade.log")
		upgradeCommand = upgrade.StableInstallCommand()
		upgradeCommandTarget = upgrade.VersionInstallCommand(version.KnownBuildVersion())
	}

	hostname, _ := os.Hostname()

	return SystemInfoResponse{
		Version:              version.BuildVersion(),
		Commit:               version.Commit,
		BuildAt:              version.BuildAt,
		GoVersion:            version.GoToolchainVersion(),
		OS:                   runtime.GOOS,
		Arch:                 runtime.GOARCH,
		PID:                  os.Getpid(),
		StartTime:            processStartTime.UTC().Format(time.RFC3339),
		Uptime:               time.Since(processStartTime).Seconds(),
		BinaryPath:           binaryPath,
		BinaryDirWritable:    binaryDirWritable,
		ConfigPath:           configPath,
		ConfigDir:            configDir,
		UpgradeLogPath:       upgradeLogPath,
		UpgradeCommand:       upgradeCommand,
		UpgradeCommandTarget: upgradeCommandTarget,
		Hostname:             hostname,
	}
}

func resolveBinaryPathInfo() (string, bool) {
	exePath, err := os.Executable()
	if err != nil {
		return "", false
	}
	realExe, err := filepath.EvalSymlinks(exePath)
	if err != nil {
		realExe = exePath
	}
	return realExe, isDirWritable(filepath.Dir(realExe))
}

func isDirWritable(dir string) bool {
	if dir == "" {
		return false
	}
	testFile, err := os.CreateTemp(dir, ".agentteam-write-check-*")
	if err != nil {
		return false
	}
	name := testFile.Name()
	_ = testFile.Close()
	_ = os.Remove(name)
	return true
}
