package app

import (
	"context"
	"testing"
	"time"

	cfgpkg "github.com/jan/fm-rds-tx/internal/config"
	"github.com/jan/fm-rds-tx/internal/platform"
)

func TestEngineContinuousRun(t *testing.T) {
	cfg := cfgpkg.Default()
	driver := platform.NewSimulatedDriver(nil)
	eng := NewEngine(cfg, driver)
	eng.SetChunkDuration(10 * time.Millisecond)

	ctx := context.Background()
	if err := eng.Start(ctx); err != nil {
		t.Fatalf("start: %v", err)
	}

	// Let it run for 200ms
	time.Sleep(200 * time.Millisecond)

	stats := eng.Stats()
	if stats.State != "running" {
		t.Fatalf("expected running, got %s", stats.State)
	}
	if stats.ChunksProduced < 5 {
		t.Fatalf("expected at least 5 chunks, got %d", stats.ChunksProduced)
	}
	if stats.TotalSamples == 0 {
		t.Fatal("expected non-zero samples")
	}

	if err := eng.Stop(ctx); err != nil {
		t.Fatalf("stop: %v", err)
	}

	stats = eng.Stats()
	if stats.State != "idle" {
		t.Fatalf("expected idle after stop, got %s", stats.State)
	}
}

func TestEngineDoubleStartFails(t *testing.T) {
	cfg := cfgpkg.Default()
	driver := platform.NewSimulatedDriver(nil)
	eng := NewEngine(cfg, driver)

	ctx := context.Background()
	if err := eng.Start(ctx); err != nil {
		t.Fatalf("first start: %v", err)
	}
	defer eng.Stop(ctx)

	if err := eng.Start(ctx); err == nil {
		t.Fatal("expected error on double start")
	}
}

func TestEngineDriverStats(t *testing.T) {
	cfg := cfgpkg.Default()
	driver := platform.NewSimulatedDriver(nil)
	eng := NewEngine(cfg, driver)
	eng.SetChunkDuration(10 * time.Millisecond)

	ctx := context.Background()
	_ = eng.Start(ctx)
	time.Sleep(100 * time.Millisecond)
	_ = eng.Stop(ctx)

	driverStats := driver.Stats()
	if driverStats.SamplesWritten == 0 {
		t.Fatal("expected driver to have written samples")
	}
	if driverStats.FramesWritten == 0 {
		t.Fatal("expected driver to have written frames")
	}
}

func TestEngineSplitRate(t *testing.T) {
	// Simulate Pluto-like config: composite at 228kHz, device at 2.28MHz
	cfg := cfgpkg.Default()
	cfg.Backend.DeviceSampleRateHz = 2280000 // 10:1 ratio

	driver := platform.NewSimulatedDriver(nil)
	eng := NewEngine(cfg, driver)
	eng.SetChunkDuration(10 * time.Millisecond)

	// Verify split-rate path was chosen
	if eng.upsampler == nil {
		t.Fatal("expected split-rate mode (upsampler != nil)")
	}

	ctx := context.Background()
	if err := eng.Start(ctx); err != nil {
		t.Fatalf("start: %v", err)
	}

	time.Sleep(200 * time.Millisecond)

	stats := eng.Stats()
	if stats.ChunksProduced < 5 {
		t.Fatalf("expected at least 5 chunks, got %d", stats.ChunksProduced)
	}
	// With 10:1 upsampling and 10ms chunks at 228kHz source:
	// 2280 src samples → 22800 output samples per chunk
	// 5 chunks minimum → at least 114000 samples
	if stats.TotalSamples < 50000 {
		t.Fatalf("expected at least 50000 upsampled samples, got %d", stats.TotalSamples)
	}

	if err := eng.Stop(ctx); err != nil {
		t.Fatalf("stop: %v", err)
	}
	if stats.Underruns > 0 {
		t.Fatalf("unexpected underruns: %d", stats.Underruns)
	}
}

func TestEngineSameRate(t *testing.T) {
	// Default config: deviceSampleRateHz=0, compositeRateHz=228000
	// EffectiveDeviceRate = 228000 → same-rate mode, no upsampler
	cfg := cfgpkg.Default()
	driver := platform.NewSimulatedDriver(nil)
	eng := NewEngine(cfg, driver)

	if eng.upsampler != nil {
		t.Fatal("expected same-rate mode (upsampler == nil)")
	}
}

func TestEngineLiveUpdateDSP(t *testing.T) {
	cfg := cfgpkg.Default()
	driver := platform.NewSimulatedDriver(nil)
	eng := NewEngine(cfg, driver)
	eng.SetChunkDuration(10 * time.Millisecond)

	ctx := context.Background()
	if err := eng.Start(ctx); err != nil {
		t.Fatalf("start: %v", err)
	}
	defer eng.Stop(ctx)

	time.Sleep(50 * time.Millisecond)

	// Update DSP params while running
	drive := 1.5
	stereo := false
	err := eng.UpdateConfig(LiveConfigUpdate{
		OutputDrive:   &drive,
		StereoEnabled: &stereo,
	})
	if err != nil {
		t.Fatalf("UpdateConfig: %v", err)
	}

	// Engine should still be running after update
	time.Sleep(50 * time.Millisecond)
	stats := eng.Stats()
	if stats.State != "running" {
		t.Fatalf("expected running after update, got %s", stats.State)
	}
	if stats.Underruns > 0 {
		t.Fatalf("unexpected underruns after update: %d", stats.Underruns)
	}
}

func TestEngineLiveUpdateFrequency(t *testing.T) {
	cfg := cfgpkg.Default()
	driver := platform.NewSimulatedDriver(nil)
	eng := NewEngine(cfg, driver)
	eng.SetChunkDuration(10 * time.Millisecond)

	ctx := context.Background()
	if err := eng.Start(ctx); err != nil {
		t.Fatalf("start: %v", err)
	}
	defer eng.Stop(ctx)

	time.Sleep(50 * time.Millisecond)

	// Tune frequency
	freq := 99.5
	err := eng.UpdateConfig(LiveConfigUpdate{FrequencyMHz: &freq})
	if err != nil {
		t.Fatalf("UpdateConfig freq: %v", err)
	}

	// Let it process for a bit so the pending freq gets applied
	time.Sleep(50 * time.Millisecond)
	stats := eng.Stats()
	if stats.State != "running" {
		t.Fatalf("expected running after tune, got %s", stats.State)
	}
}

func TestEngineLiveUpdateRDS(t *testing.T) {
	cfg := cfgpkg.Default()
	driver := platform.NewSimulatedDriver(nil)
	eng := NewEngine(cfg, driver)
	eng.SetChunkDuration(10 * time.Millisecond)

	ctx := context.Background()
	if err := eng.Start(ctx); err != nil {
		t.Fatalf("start: %v", err)
	}
	defer eng.Stop(ctx)

	time.Sleep(50 * time.Millisecond)

	// Update RDS text
	ps := "NEWPS"
	rt := "Now playing: test track"
	err := eng.UpdateConfig(LiveConfigUpdate{PS: &ps, RadioText: &rt})
	if err != nil {
		t.Fatalf("UpdateConfig RDS: %v", err)
	}

	time.Sleep(50 * time.Millisecond)
	stats := eng.Stats()
	if stats.Underruns > 0 {
		t.Fatalf("underruns after RDS update: %d", stats.Underruns)
	}
}

func TestEngineLiveUpdateValidation(t *testing.T) {
	cfg := cfgpkg.Default()
	driver := platform.NewSimulatedDriver(nil)
	eng := NewEngine(cfg, driver)

	// Out of range frequency
	badFreq := 200.0
	if err := eng.UpdateConfig(LiveConfigUpdate{FrequencyMHz: &badFreq}); err == nil {
		t.Fatal("expected validation error for bad frequency")
	}

	// Out of range drive
	badDrive := 11.0
	if err := eng.UpdateConfig(LiveConfigUpdate{OutputDrive: &badDrive}); err == nil {
		t.Fatal("expected validation error for bad drive")
	}

	// Valid update should succeed
	goodDrive := 1.0
	if err := eng.UpdateConfig(LiveConfigUpdate{OutputDrive: &goodDrive}); err != nil {
		t.Fatalf("expected valid update to succeed: %v", err)
	}
}
