//go:build linux

package proc

import (
	"bytes"
	"encoding/binary"
	"os"
	"strings"
	"testing"
	"time"
)

func TestParseLinuxProcStatHandlesClosingParenthesisInCommand(t *testing.T) {
	const stat = "321 (worker ) pool)) S 12 34 56 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 987654 0 0"

	ppid, pgid, startTicks, ok := parseLinuxProcStat(stat)
	if !ok {
		t.Fatal("parseLinuxProcStat rejected valid fixture")
	}
	if ppid != 12 || pgid != 34 || startTicks != 987654 {
		t.Fatalf("parseLinuxProcStat = (%d, %d, %d), want (12, 34, 987654)", ppid, pgid, startTicks)
	}
}

func TestLinuxStartedAt(t *testing.T) {
	bootTime := time.Date(2026, 7, 24, 0, 0, 0, 0, time.UTC)
	got := linuxStartedAt(bootTime, 12345, 250)
	want := bootTime.Add(49*time.Second + 380*time.Millisecond)
	if !got.Equal(want) {
		t.Fatalf("linuxStartedAt = %v, want %v", got, want)
	}
}

func TestLinuxProcessStartIDDoesNotDependOnWallClock(t *testing.T) {
	const (
		bootID     = "8b98170f-2fcb-4e16-a71d-accfefcc9055"
		startTicks = 2_121_784
	)
	if got, want := linuxProcessStartID(bootID, startTicks), "linux:"+bootID+":2121784"; got != want {
		t.Fatalf("linuxProcessStartID() = %q, want %q", got, want)
	}
}

func TestNormalizeLinuxBootID(t *testing.T) {
	const bootID = "8b98170f-2fcb-4e16-a71d-accfefcc9055"
	if got, ok := normalizeLinuxBootID("  " + strings.ToUpper(bootID) + "\n"); !ok || got != bootID {
		t.Fatalf("normalizeLinuxBootID() = (%q, %v), want (%q, true)", got, ok, bootID)
	}
	for _, invalid := range []string{
		"",
		"8b98170f-2fcb-4e16-a71d-accfefcc905",
		"8b98170f:2fcb-4e16-a71d-accfefcc9055",
		"8b98170f-2fcb-4e16-a71d-accfefcc905g",
	} {
		if _, ok := normalizeLinuxBootID(invalid); ok {
			t.Fatalf("normalizeLinuxBootID(%q) succeeded, want failure", invalid)
		}
	}
}

func TestParseLinuxAuxvClockTicks(t *testing.T) {
	var auxv bytes.Buffer
	for _, value := range []uint64{
		6, 4096,
		17, 250,
		0, 0,
	} {
		if err := binary.Write(&auxv, binary.LittleEndian, value); err != nil {
			t.Fatal(err)
		}
	}

	ticks, ok := parseLinuxAuxvClockTicks(auxv.Bytes(), 8, binary.LittleEndian)
	if !ok || ticks != 250 {
		t.Fatalf("parseLinuxAuxvClockTicks = (%d, %v), want (250, true)", ticks, ok)
	}
}

func TestProcessByPID(t *testing.T) {
	process, ok := ProcessByPID(os.Getpid())
	if !ok {
		t.Fatal("ProcessByPID did not find the current process")
	}
	if process.PID != os.Getpid() {
		t.Fatalf("PID = %d, want %d", process.PID, os.Getpid())
	}
	if process.StartedAt.IsZero() {
		t.Fatal("StartedAt is zero")
	}
	if process.StartID == "" {
		t.Fatal("StartID is empty")
	}
	if process.StartedAt.Location() != time.UTC {
		t.Fatalf("StartedAt location = %v, want UTC", process.StartedAt.Location())
	}
}
