package metrics

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"zlh-agent/internal/state"
	"zlh-agent/internal/system"
)

type ProcessSection struct {
	PID           int    `json:"pid"`
	Status        string `json:"status"`
	UptimeSeconds *int64 `json:"uptime_seconds"`
	RestartCount  int    `json:"restart_count"`
}

type MemorySection struct {
	RSSBytes int64 `json:"rss_bytes"`
	VMSBytes int64 `json:"vms_bytes"`
}

type ProcessMetricsResponse struct {
	Process ProcessSection `json:"process"`
	Memory  MemorySection  `json:"memory"`
}

func ProcessMetrics() (ProcessMetricsResponse, bool, error) {
	pid, ok := system.GetServerPID()
	if !ok {
		return ProcessMetricsResponse{
			Process: ProcessSection{
				PID:          0,
				Status:       "stopped",
				RestartCount: state.GetCrashCount(),
			},
		}, true, nil
	}

	rss, vms, err := readMemoryFromStatus(pid)
	if err != nil {
		return ProcessMetricsResponse{}, false, err
	}

	return ProcessMetricsResponse{
		Process: ProcessSection{
			PID:           pid,
			Status:        "running",
			UptimeSeconds: nil,
			RestartCount:  state.GetCrashCount(),
		},
		Memory: MemorySection{
			RSSBytes: rss,
			VMSBytes: vms,
		},
	}, false, nil
}

func readMemoryFromStatus(pid int) (int64, int64, error) {
	statusPath := filepath.Join("/proc", strconv.Itoa(pid), "status")
	f, err := os.Open(statusPath)
	if err != nil {
		return 0, 0, err
	}
	defer f.Close()

	var rssKB int64
	var vmsKB int64

	s := bufio.NewScanner(f)
	for s.Scan() {
		line := s.Text()
		if strings.HasPrefix(line, "VmRSS:") {
			rssKB = parseKBLine(line)
		}
		if strings.HasPrefix(line, "VmSize:") {
			vmsKB = parseKBLine(line)
		}
	}
	if err := s.Err(); err != nil {
		return 0, 0, err
	}
	if rssKB == 0 && vmsKB == 0 {
		return 0, 0, fmt.Errorf("missing VmRSS/VmSize in %s", statusPath)
	}
	return rssKB * 1024, vmsKB * 1024, nil
}

func parseKBLine(line string) int64 {
	fields := strings.Fields(line)
	if len(fields) < 2 {
		return 0
	}
	n, err := strconv.ParseInt(fields[1], 10, 64)
	if err != nil {
		return 0
	}
	return n
}
