package output

import (
	"context"
	"testing"
	"time"
)

func TestFrameQueuePushPop(t *testing.T) {
	q := NewFrameQueue(2)
	ctx := context.Background()

	frame := &CompositeFrame{Sequence: 1}
	if err := q.Push(ctx, frame); err != nil {
		t.Fatalf("push failed: %v", err)
	}
	if got := q.Depth(); got != 1 {
		t.Fatalf("expected depth 1, got %d", got)
	}
	if got := q.FillLevel(); got <= 0 || got >= 1 {
		t.Fatalf("unexpected fill level: %f", got)
	}

	popped, err := q.Pop(ctx)
	if err != nil {
		t.Fatalf("pop failed: %v", err)
	}
	if popped != frame {
		t.Fatal("popped frame differs from pushed frame")
	}
	if q.Depth() != 0 {
		t.Fatalf("expected depth 0 after pop, got %d", q.Depth())
	}

	stats := q.Stats()
	if stats.HighWaterMark == 0 {
		t.Fatal("expected high water mark to track push")
	}
	if stats.LowWaterMark != 0 {
		t.Fatalf("expected low water mark 0, got %d", stats.LowWaterMark)
	}
}

func TestFrameQueuePushTimeout(t *testing.T) {
	q := NewFrameQueue(1)
	ctx := context.Background()
	frame := &CompositeFrame{Sequence: 42}

	if err := q.Push(ctx, frame); err != nil {
		t.Fatalf("initial push: %v", err)
	}

	shortCtx, cancel := context.WithTimeout(ctx, 5*time.Millisecond)
	defer cancel()
	if err := q.Push(shortCtx, frame); err == nil {
		t.Fatalf("expected timeout when pushing into full queue")
	}
	stats := q.Stats()
	if stats.PushTimeouts == 0 {
		t.Fatalf("expected push timeout counter to increment, got %d", stats.PushTimeouts)
	}

	_, _ = q.Pop(ctx)
}

func TestFrameQueueCounters(t *testing.T) {
	q := NewFrameQueue(1)
	q.RecordDrop()
	q.RecordRepeat()
	q.RecordMute()

	stats := q.Stats()
	if stats.DroppedFrames != 1 {
		t.Fatalf("expected 1 drop, got %d", stats.DroppedFrames)
	}
	if stats.RepeatedFrames != 1 {
		t.Fatalf("expected 1 repeat, got %d", stats.RepeatedFrames)
	}
	if stats.MutedFrames != 1 {
		t.Fatalf("expected 1 mute, got %d", stats.MutedFrames)
	}
}

func TestFrameQueueHealthIndicator(t *testing.T) {
	q := NewFrameQueue(4)
	ctx := context.Background()

	stats := q.Stats()
	if stats.Health != QueueHealthCritical {
		t.Fatalf("expected initial health critical, got %s", stats.Health)
	}

	push := func(seq uint64) {
		frame := &CompositeFrame{Sequence: seq}
		if err := q.Push(ctx, frame); err != nil {
			t.Fatalf("push %d failed: %v", seq, err)
		}
	}

	push(1)
	stats = q.Stats()
	if stats.Health != QueueHealthLow {
		t.Fatalf("expected low after one frame, got %s", stats.Health)
	}

	push(2)
	stats = q.Stats()
	if stats.Health != QueueHealthLow {
		t.Fatalf("expected low at 50%% fill, got %s", stats.Health)
	}

	push(3)
	stats = q.Stats()
	if stats.Health != QueueHealthNormal {
		t.Fatalf("expected normal once queue has ~75%% fill, got %s", stats.Health)
	}

	for q.Depth() > 0 {
		if _, err := q.Pop(ctx); err != nil {
			t.Fatalf("cleanup pop failed: %v", err)
		}
	}
}
