package tuning

import (
	"path/filepath"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

func TestTimeoutLessonsStore(t *testing.T) {
	tmpDir := t.TempDir()
	store := NewTimeoutLessonsStore(tmpDir)

	lesson := &TimeoutLesson{
		ActivityType:    "activity1",
		OldTimeout:      5 * time.Second,
		NewTimeout:      10 * time.Second,
		Reason:          "P99 exceeded",
		FailureRate:     0.2,
		SampleSize:      10,
		ConfidenceScore: 0.85,
		AppliedAt:       time.Now(),
	}

	// Append lesson
	err := store.AppendLesson("task1", lesson)
	assert.NoError(t, err)

	// Read lessons
	lessons, err := store.ReadLessons("task1")
	assert.NoError(t, err)
	assert.Equal(t, 1, len(lessons))
	assert.Equal(t, "activity1", lessons[0].ActivityType)
}

func TestGetLatestLesson(t *testing.T) {
	tmpDir := t.TempDir()
	store := NewTimeoutLessonsStore(tmpDir)

	lesson1 := &TimeoutLesson{
		ActivityType:    "activity1",
		OldTimeout:      5 * time.Second,
		NewTimeout:      10 * time.Second,
		AppliedAt:       time.Now().Add(-1 * time.Hour),
	}

	lesson2 := &TimeoutLesson{
		ActivityType:    "activity1",
		OldTimeout:      10 * time.Second,
		NewTimeout:      15 * time.Second,
		AppliedAt:       time.Now(),
	}

	store.AppendLesson("task1", lesson1)
	store.AppendLesson("task1", lesson2)

	latest, err := store.GetLatestLesson("task1")
	assert.NoError(t, err)
	assert.NotNil(t, latest)
	assert.Equal(t, 15*time.Second, latest.NewTimeout)
}

func TestEmptyLessons(t *testing.T) {
	tmpDir := t.TempDir()
	store := NewTimeoutLessonsStore(tmpDir)

	lessons, err := store.ReadLessons("nonexistent_task")
	assert.NoError(t, err)
	assert.Nil(t, lessons)

	latest, err := store.GetLatestLesson("nonexistent_task")
	assert.NoError(t, err)
	assert.Nil(t, latest)
}

func TestGenerateLessonFromRecommendation(t *testing.T) {
	rec := &TimeoutRecommendation{
		ActivityType:       "activity1",
		CurrentTimeout:     5 * time.Second,
		RecommendedTimeout: 10 * time.Second,
		P95Duration:        8 * time.Second,
		FailureCount:       2,
		SuccessCount:       8,
		Confidence:         0.95,
		Reason:             "P95 exceeded",
		Timestamp:          time.Now(),
	}

	lesson := GenerateLessonFromRecommendation(rec)
	assert.NotNil(t, lesson)
	assert.Equal(t, "activity1", lesson.ActivityType)
	assert.Equal(t, 5*time.Second, lesson.OldTimeout)
	assert.Equal(t, 10*time.Second, lesson.NewTimeout)
	assert.Equal(t, 0.2, lesson.FailureRate)
	assert.Equal(t, 10, lesson.SampleSize)
}

func TestGenerateLessonFromNilRecommendation(t *testing.T) {
	lesson := GenerateLessonFromRecommendation(nil)
	assert.Nil(t, lesson)
}

func TestFormatLessonsForPlanner(t *testing.T) {
	lessons := []*TimeoutLesson{
		{
			ActivityType:    "activity1",
			OldTimeout:      5 * time.Second,
			NewTimeout:      10 * time.Second,
			Reason:          "P95 exceeded",
			ConfidenceScore: 0.95,
		},
		{
			ActivityType:    "activity2",
			OldTimeout:      3 * time.Second,
			NewTimeout:      6 * time.Second,
			Reason:          "Timeout too tight",
			ConfidenceScore: 0.75,
		},
	}

	formatted := FormatLessonsForPlanner(lessons)
	assert.Contains(t, formatted, "activity1")
	assert.Contains(t, formatted, "activity2")
	assert.Contains(t, formatted, "P95 exceeded")
	assert.Contains(t, formatted, "95.0%")
}

func TestFormatEmptyLessons(t *testing.T) {
	formatted := FormatLessonsForPlanner(nil)
	assert.Equal(t, "No timeout lessons available.", formatted)

	formatted = FormatLessonsForPlanner([]*TimeoutLesson{})
	assert.Equal(t, "No timeout lessons available.", formatted)
}

func TestGenerateSignalsFromRecommendations(t *testing.T) {
	recommendations := []TimeoutRecommendation{
		{
			ActivityType:       "activity1",
			RecommendedTimeout: 10 * time.Second,
			Reason:             "P95 exceeded",
			Confidence:         0.95,
		},
		{
			ActivityType:       "activity2",
			RecommendedTimeout: 5 * time.Second,
			Reason:             "Timeout reduced",
			Confidence:         0.55,
		},
		{
			ActivityType:       "activity3",
			RecommendedTimeout: 3 * time.Second,
			Reason:             "Low priority",
			Confidence:         0.45,
		},
	}

	signals := GenerateSignalsFromRecommendations(recommendations)
	assert.Equal(t, 3, len(signals))

	// Check priority levels
	assert.Equal(t, "high", signals[0].Priority)
	assert.Equal(t, "medium", signals[1].Priority)
	assert.Equal(t, "low", signals[2].Priority)
}

func TestSignalStructure(t *testing.T) {
	recommendations := []TimeoutRecommendation{
		{
			ActivityType:       "activity1",
			CurrentTimeout:     5 * time.Second,
			RecommendedTimeout: 10 * time.Second,
			Reason:             "P95 exceeded",
			Confidence:         0.85,
		},
	}

	signals := GenerateSignalsFromRecommendations(recommendations)
	assert.Greater(t, len(signals), 0)

	signal := signals[0]
	assert.Equal(t, "activity1", signal.ActivityType)
	assert.Equal(t, 10*time.Second, signal.NewTimeout)
	assert.Equal(t, "P95 exceeded", signal.Reason)
	assert.Equal(t, 0.85, signal.Confidence)
}

func TestMultipleLessonAppends(t *testing.T) {
	tmpDir := t.TempDir()
	store := NewTimeoutLessonsStore(tmpDir)

	// Append multiple lessons
	for i := 0; i < 5; i++ {
		lesson := &TimeoutLesson{
			ActivityType:    "activity1",
			OldTimeout:      time.Duration(i*5) * time.Second,
			NewTimeout:      time.Duration((i+1)*5) * time.Second,
		}
		err := store.AppendLesson("task1", lesson)
		assert.NoError(t, err)
	}

	lessons, err := store.ReadLessons("task1")
	assert.NoError(t, err)
	assert.Equal(t, 5, len(lessons))
}

func TestLessonPersistence(t *testing.T) {
	tmpDir := t.TempDir()
	store1 := NewTimeoutLessonsStore(tmpDir)

	lesson := &TimeoutLesson{
		ActivityType:    "activity1",
		OldTimeout:      5 * time.Second,
		NewTimeout:      10 * time.Second,
	}

	store1.AppendLesson("task1", lesson)

	// Create new store instance
	store2 := NewTimeoutLessonsStore(tmpDir)
	lessons, err := store2.ReadLessons("task1")
	assert.NoError(t, err)
	assert.Equal(t, 1, len(lessons))
	assert.Equal(t, 10*time.Second, lessons[0].NewTimeout)
}

func TestLessonEffectivenessTracking(t *testing.T) {
	lesson := &TimeoutLesson{
		ActivityType:    "activity1",
		OldTimeout:      5 * time.Second,
		NewTimeout:      10 * time.Second,
		Effective:       false,
	}

	assert.False(t, lesson.Effective)

	lesson.Effective = true
	assert.True(t, lesson.Effective)
}

func TestHighConfidenceSignal(t *testing.T) {
	recommendations := []TimeoutRecommendation{
		{
			ActivityType:       "activity1",
			RecommendedTimeout: 10 * time.Second,
			Reason:             "Very confident",
			Confidence:         0.99,
		},
	}

	signals := GenerateSignalsFromRecommendations(recommendations)
	assert.Equal(t, "high", signals[0].Priority)
}

func TestLessonFileLayout(t *testing.T) {
	tmpDir := t.TempDir()
	store := NewTimeoutLessonsStore(tmpDir)

	store.AppendLesson("task1", &TimeoutLesson{
		ActivityType: "activity1",
	})

	// Verify file layout
	expectedPath := filepath.Join(tmpDir, "tuning", "lessons", "task1_timeout_lessons.jsonl")
	assert.DirExists(t, filepath.Dir(expectedPath))
}
