package latencylog

import (
	"bufio"
	"encoding/json"
	"os"
	"path/filepath"
	"sync"
	"testing"

	"omnisocketgo/cmd/internal/protocol"
)

func TestJSONLLoggerWritesOneEventPerLine(t *testing.T) {
	path := filepath.Join(t.TempDir(), "latency.jsonl")

	logger, err := NewJSONLLogger(path)
	if err != nil {
		t.Fatalf("NewJSONLLogger() error = %v", err)
	}
	t.Cleanup(func() {
		_ = logger.Close()
	})

	event := Event{
		TsUnixNano:  123,
		NodeRole:    NodeRolePeer,
		NodeID:      "peer-a",
		Event:       EventAAppPrepBegin,
		MessageType: protocol.MessageTypeText,
		MessageID:   1,
		From:        "peer-a",
		To:          "peer-b",
		BodySize:    5,
	}
	if err := logger.LogEvent(event); err != nil {
		t.Fatalf("LogEvent() error = %v", err)
	}

	file, err := os.Open(path)
	if err != nil {
		t.Fatalf("os.Open() error = %v", err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	if !scanner.Scan() {
		t.Fatal("expected one JSONL line, got none")
	}

	var got Event
	if err := json.Unmarshal(scanner.Bytes(), &got); err != nil {
		t.Fatalf("json.Unmarshal() error = %v", err)
	}
	if got != event {
		t.Fatalf("event mismatch: got %+v want %+v", got, event)
	}
	if scanner.Scan() {
		t.Fatal("expected exactly one JSONL line")
	}
	if err := scanner.Err(); err != nil {
		t.Fatalf("scanner.Err() = %v", err)
	}
}

func TestJSONLLoggerHandlesConcurrentWrites(t *testing.T) {
	path := filepath.Join(t.TempDir(), "latency.jsonl")

	logger, err := NewJSONLLogger(path)
	if err != nil {
		t.Fatalf("NewJSONLLogger() error = %v", err)
	}
	t.Cleanup(func() {
		_ = logger.Close()
	})

	const total = 32

	var wg sync.WaitGroup
	for i := 0; i < total; i++ {
		i := i
		wg.Add(1)
		go func() {
			defer wg.Done()

			err := logger.LogEvent(Event{
				TsUnixNano:  int64(i + 1),
				NodeRole:    NodeRoleServer,
				NodeID:      protocol.ServerPeerID,
				Event:       EventBAppRecv,
				MessageType: protocol.MessageTypeFile,
				MessageID:   uint64(i + 1),
				From:        "peer-a",
				To:          "peer-b",
				FileName:    "payload.bin",
				BodySize:    3,
			})
			if err != nil {
				t.Errorf("LogEvent() error = %v", err)
			}
		}()
	}
	wg.Wait()

	file, err := os.Open(path)
	if err != nil {
		t.Fatalf("os.Open() error = %v", err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	var count int
	seen := make(map[uint64]bool, total)
	for scanner.Scan() {
		var event Event
		if err := json.Unmarshal(scanner.Bytes(), &event); err != nil {
			t.Fatalf("json.Unmarshal() error = %v", err)
		}
		count++
		seen[event.MessageID] = true
	}
	if err := scanner.Err(); err != nil {
		t.Fatalf("scanner.Err() = %v", err)
	}
	if count != total {
		t.Fatalf("line count = %d, want %d", count, total)
	}
	if len(seen) != total {
		t.Fatalf("unique message count = %d, want %d", len(seen), total)
	}
}
