package api

import (
	"math"
	"net/http"
	"os"
	"runtime"
	"time"

	"github.com/gin-gonic/gin"
)

// 构建信息由编译参数注入:
// go build -ldflags "-X oci-portal/internal/api.buildVersion=v1.0.0 -X oci-portal/internal/api.buildTime=2026-07-09T12:00Z"
var (
	buildVersion = "dev"
	buildTime    = ""
	// startedAt 为进程启动时刻,运行时长与 CPU 均值的计算基准
	startedAt = time.Now()
	// aboutDB 由 main 启动时注入,存储指标据此定位数据库文件
	aboutDB struct{ driver, path string }
)

// SetAboutRuntime 注入数据库形态,「关于」页存储指标展示用;启动时调用一次。
func SetAboutRuntime(dbDriver, dbPath string) {
	aboutDB.driver, aboutDB.path = dbDriver, dbPath
}

// about 返回构建与运行环境信息,「设置 · 关于」页展示。
//
// @Summary  返回构建、运行时长与资源占用信息,「设置 · 关于」页展示
// @Tags     设置
// @Success  200 {object} aboutResponse
// @Security BearerAuth
// @Router   /api/v1/about [get]
func about(c *gin.Context) {
	c.JSON(http.StatusOK, gin.H{
		"version":       buildVersion,
		"buildTime":     buildTime,
		"goVersion":     runtime.Version(),
		"platform":      runtime.GOOS + "/" + runtime.GOARCH,
		"startedAt":     startedAt.UTC().Format(time.RFC3339),
		"uptimeSeconds": int64(time.Since(startedAt).Seconds()),
		"resources":     resourcesSnapshot(),
	})
}

// resourcesSnapshot 采集进程资源占用:CPU 自启动均值、内存、goroutine 与存储。
func resourcesSnapshot() gin.H {
	var ms runtime.MemStats
	runtime.ReadMemStats(&ms)
	pct := 0.0
	if up := time.Since(startedAt).Seconds(); up > 0 {
		// 多核并行时均值可超 100%,口径为「占单核百分比」
		pct = processCPUTime().Seconds() / up * 100
	}
	return gin.H{
		"cpuAvgPercent": math.Round(pct*100) / 100,
		"numCpu":        runtime.NumCPU(),
		"goroutines":    runtime.NumGoroutine(),
		"memHeapBytes":  ms.HeapAlloc,
		"memSysBytes":   ms.Sys,
		"dbEngine":      aboutDB.driver,
		"dbBytes":       dbFileBytes(aboutDB.driver, aboutDB.path),
	}
}

// dbFileBytes 统计数据库磁盘占用:sqlite 累加主文件与 -wal / -shm,
// 外部数据库(mysql / postgres)不可从本机度量,返回 -1 表示不可用。
func dbFileBytes(driver, path string) int64 {
	if driver != "sqlite" || path == "" {
		return -1
	}
	var total int64
	for _, p := range []string{path, path + "-wal", path + "-shm"} {
		if fi, err := os.Stat(p); err == nil {
			total += fi.Size()
		}
	}
	return total
}
