package services

import (
	"bookhoard/internal/database"
	"context"
	"os"
	"path/filepath"
	"testing"
	"time"

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

// Helper function to setup test database
func setupTestDB(t *testing.T) *database.Queries {
	// Use existing test database setup
	// This would connect to the test database
	return &database.Queries{} // Placeholder - use your actual test DB setup
}
func TestMarkDirectoryDirty(t *testing.T) {
	db := setupTestDB(t)
	scanner := NewMediaScanner(db)
	scanner.folders = []string{"/test/folder"}
	scanner.markDirectoryDirty("/test/folder/subdir")
	scanner.dirtyDirsMu.RLock()
	_, exists := scanner.dirtyDirs["/test/folder/subdir"]
	scanner.dirtyDirsMu.RUnlock()
	assert.True(t, exists, "Directory should be marked dirty")
}
func TestMarkDirectoryDirty_IgnoresNonWatchedPaths(t *testing.T) {
	db := setupTestDB(t)
	scanner := NewMediaScanner(db)
	scanner.folders = []string{"/test/folder"}
	scanner.markDirectoryDirty("/other/folder")
	scanner.dirtyDirsMu.RLock()
	_, exists := scanner.dirtyDirs["/other/folder"]
	scanner.dirtyDirsMu.RUnlock()
	assert.False(t, exists, "Non-watched directory should be ignored")
}
func TestMarkDirectoryDirty_SmartEventMerging(t *testing.T) {
	db := setupTestDB(t)
	scanner := NewMediaScanner(db)
	scanner.folders = []string{"/test/folder"}
	// Mark subdirectory first
	scanner.markDirectoryDirty("/test/folder/subdir1")
	scanner.dirtyDirsMu.RLock()
	_, exists1 := scanner.dirtyDirs["/test/folder/subdir1"]
	scanner.dirtyDirsMu.RUnlock()
	assert.True(t, exists1)
	// Mark parent directory - should replace subdirectory
	scanner.markDirectoryDirty("/test/folder")
	scanner.dirtyDirsMu.RLock()
	_, parentExists := scanner.dirtyDirs["/test/folder"]
	_, childExists := scanner.dirtyDirs["/test/folder/subdir1"]
	scanner.dirtyDirsMu.RUnlock()
	assert.True(t, parentExists, "Parent should exist")
	assert.False(t, childExists, "Child should be removed (consolidated)")
}
func TestWaitForFileStability_StableFile(t *testing.T) {
	db := setupTestDB(t)
	scanner := NewMediaScanner(db)
	// Create a stable file
	tmpDir := t.TempDir()
	filePath := filepath.Join(tmpDir, "stable.epub")
	err := os.WriteFile(filePath, []byte("test content"), 0644)
	require.NoError(t, err)
	// Should return true immediately (file already stable)
	assert.True(t, scanner.waitForFileStability(filePath))
}
func TestWaitForFileStability_UnstableFile(t *testing.T) {
	db := setupTestDB(t)
	scanner := NewMediaScanner(db)
	// Create a file
	tmpDir := t.TempDir()
	filePath := filepath.Join(tmpDir, "unstable.epub")
	file, err := os.Create(filePath)
	require.NoError(t, err)
	defer file.Close()
	// Start stability check in background
	stableChan := make(chan bool)
	go func() {
		stableChan <- scanner.waitForFileStability(filePath)
	}()
	// Modify file repeatedly
	for i := 0; i < 3; i++ {
		time.Sleep(100 * time.Millisecond)
		file.WriteString("more data\n")
	}
	file.Close()
	// Should eventually return true
	select {
	case stable := <-stableChan:
		assert.True(t, stable)
	case <-time.After(7 * time.Second):
		t.Fatal("waitForFileStability timeout")
	}
}
func TestProcessDirtyDirectories_BatchesScans(t *testing.T) {
	db := setupTestDB(t)
	scanner := NewMediaScanner(db)
	scanner.folders = []string{"/test/folder"}
	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
	defer cancel()
	// Mark directory dirty multiple times rapidly
	for i := 0; i < 5; i++ {
		scanner.markDirectoryDirty("/test/folder/subdir")
		time.Sleep(100 * time.Millisecond)
	}
	go scanner.processDirtyDirectories(ctx)
	// Should wait 10 seconds before processing
	scanner.dirtyDirsMu.RLock()
	count := len(scanner.dirtyDirs)
	scanner.dirtyDirsMu.RUnlock()
	assert.Equal(t, 1, count, "Directory should still be in dirty list")
	// Wait for batch to complete
	time.Sleep(15 * time.Second)
	scanner.dirtyDirsMu.RLock()
	count = len(scanner.dirtyDirs)
	scanner.dirtyDirsMu.RUnlock()
	assert.Equal(t, 0, count, "All dirty directories should be processed after 10s")
}
