package services

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

	"bookhoard/internal/database"

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

func setupTestDB(t *testing.T) *database.Queries {
	return &database.Queries{}
}

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"}
	scanner.markDirectoryDirty("/test/folder/subdir1")
	scanner.dirtyDirsMu.RLock()
	_, exists1 := scanner.dirtyDirs["/test/folder/subdir1"]
	scanner.dirtyDirsMu.RUnlock()
	assert.True(t, exists1)
	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)
	tmpDir := t.TempDir()
	filePath := filepath.Join(tmpDir, "stable.epub")
	err := os.WriteFile(filePath, []byte("test content"), 0644)
	require.NoError(t, err)
	assert.True(t, scanner.waitForFileStability(filePath))
}

func TestWaitForFileStability_UnstableFile(t *testing.T) {
	db := setupTestDB(t)
	scanner := NewMediaScanner(db)
	tmpDir := t.TempDir()
	filePath := filepath.Join(tmpDir, "unstable.epub")
	file, err := os.Create(filePath)
	require.NoError(t, err)
	defer file.Close()
	stableChan := make(chan bool)
	go func() {
		stableChan <- scanner.waitForFileStability(filePath)
	}()
	for i := 0; i < 3; i++ {
		time.Sleep(100 * time.Millisecond)
		file.WriteString("more data\n")
	}
	file.Close()
	select {
	case stable := <-stableChan:
		assert.True(t, stable)
	case <-time.After(7 * time.Second):
		t.Fatal("waitForFileStability timeout")
	}
}

func TestProcessDirtyDirectories_CollectsReadyDirs(t *testing.T) {
	db := setupTestDB(t)
	scanner := NewMediaScanner(db)
	scanner.folders = []string{"/test/folder"}

	scanner.dirtyDirsMu.Lock()
	scanner.dirtyDirs["/test/folder/subdir"] = time.Now().Add(-15 * time.Second)
	scanner.dirtyDirsMu.Unlock()

	scanner.dirtyDirsMu.Lock()
	now := time.Now()
	readyDirs := make([]string, 0)
	for dirPath, lastChange := range scanner.dirtyDirs {
		if now.Sub(lastChange) >= 10*time.Second {
			readyDirs = append(readyDirs, dirPath)
			delete(scanner.dirtyDirs, dirPath)
		}
	}
	scanner.dirtyDirsMu.Unlock()

	assert.Equal(t, 1, len(readyDirs), "Should find one ready directory")
	assert.Equal(t, "/test/folder/subdir", readyDirs[0])

	scanner.dirtyDirsMu.RLock()
	count := len(scanner.dirtyDirs)
	scanner.dirtyDirsMu.RUnlock()
	assert.Equal(t, 0, count, "Ready directory should be removed from dirty list")
}
