package infra

import (
	"testing"
	"time"
)

// openTestVaultDB creates a fresh vault_index.db in a temp dir and returns the path.
func openTestVaultDir(t *testing.T) string {
	t.Helper()
	dir := t.TempDir()
	db, err := VaultIndexOpen(dir)
	if err != nil {
		t.Fatalf("VaultIndexOpen: %v", err)
	}
	db.Close()
	return dir
}

// seedVaultFile inserts a row into files + files_fts.
func seedVaultFile(t *testing.T, dir, relPath, mime, bucket, subBucket, contentText string, size int64) {
	t.Helper()
	db, err := VaultIndexOpen(dir)
	if err != nil {
		t.Fatalf("VaultIndexOpen seed: %v", err)
	}
	defer db.Close()

	now := time.Now().Unix()
	_, err = db.Exec(`
		INSERT INTO files (rel_path, size, mtime, sha256, mime, ext, bucket, sub_bucket, indexed_at)
		VALUES (?, ?, ?, 'aabbccdd', ?, '', ?, ?, ?)`,
		relPath, size, now, mime, bucket, subBucket, now,
	)
	if err != nil {
		t.Fatalf("seed files: %v", err)
	}
	_, err = db.Exec(`INSERT INTO files_fts(rel_path, content_text) VALUES (?, ?)`, relPath, contentText)
	if err != nil {
		t.Fatalf("seed files_fts: %v", err)
	}
}

// --- Tests ---

func TestVaultSearch_FTSMatch(t *testing.T) {
	t.Run("FTS match devuelve hit con snippet", func(t *testing.T) {
		dir := openTestVaultDir(t)
		seedVaultFile(t, dir, "data/raw/informe.csv", "text/csv", "data", "raw",
			"ventas trimestrales empresa iberica", 1024)
		seedVaultFile(t, dir, "data/raw/other.csv", "text/csv", "data", "raw",
			"productos inventario almacen", 512)

		hits, err := VaultSearch(dir, "ventas", 10)
		if err != nil {
			t.Fatalf("VaultSearch: %v", err)
		}
		if len(hits) != 1 {
			t.Fatalf("got %d hits, want 1", len(hits))
		}
		if hits[0].RelPath != "data/raw/informe.csv" {
			t.Errorf("RelPath = %q, want data/raw/informe.csv", hits[0].RelPath)
		}
		if hits[0].VaultName == "" {
			t.Errorf("VaultName should not be empty")
		}
	})
}

func TestVaultSearch_NoMatch(t *testing.T) {
	t.Run("query sin resultados retorna slice vacio", func(t *testing.T) {
		dir := openTestVaultDir(t)
		seedVaultFile(t, dir, "data/raw/file.csv", "text/csv", "data", "raw", "some content", 100)

		hits, err := VaultSearch(dir, "zzznomatch", 10)
		if err != nil {
			t.Fatalf("VaultSearch: %v", err)
		}
		if len(hits) != 0 {
			t.Errorf("got %d hits, want 0", len(hits))
		}
	})
}

func TestVaultSearch_LimitRespected(t *testing.T) {
	t.Run("limit se respeta", func(t *testing.T) {
		dir := openTestVaultDir(t)
		for i := 0; i < 10; i++ {
			path := "data/raw/file" + string(rune('a'+i)) + ".csv"
			seedVaultFile(t, dir, path, "text/csv", "data", "raw", "common keyword everywhere", 100)
		}

		hits, err := VaultSearch(dir, "common", 3)
		if err != nil {
			t.Fatalf("VaultSearch: %v", err)
		}
		if len(hits) != 3 {
			t.Errorf("got %d hits, want 3", len(hits))
		}
	})
}

func TestVaultSearch_BadFTSQuery_FallbackLike(t *testing.T) {
	t.Run("query FTS invalida activa fallback LIKE", func(t *testing.T) {
		dir := openTestVaultDir(t)
		// Insert a file whose rel_path contains "foobar" so LIKE can find it.
		seedVaultFile(t, dir, "data/raw/foobar_report.csv", "text/csv", "data", "raw", "", 200)

		// "foo:bar:" — colon after a non-column name triggers FTS5 parser error.
		// safeFTSQuery passes it through unchanged because it contains ":"
		// → FTS5 "no such column: bar" → fallback LIKE on rel_path.
		hits, err := VaultSearch(dir, "foo:bar:", 10)
		if err != nil {
			t.Fatalf("VaultSearch: %v", err)
		}
		if len(hits) == 0 {
			t.Errorf("expected fallback LIKE to find foobar_report.csv, got 0 hits")
		}
		for _, h := range hits {
			if h.Snippet != "" {
				t.Errorf("fallback hits should have empty Snippet, got %q", h.Snippet)
			}
		}
	})
}

func TestVaultSearch_LimitZeroDefaults(t *testing.T) {
	t.Run("limit cero usa 50 por defecto", func(t *testing.T) {
		dir := openTestVaultDir(t)
		// Insert 55 files with the same keyword.
		for i := 0; i < 55; i++ {
			path := "data/raw/doc" + string(rune('a')) + string(rune(int('0')+i%10)) + ".csv"
			if i >= 10 {
				path = "data/raw/doc" + string(rune('b'+i/10-1)) + string(rune(int('0')+i%10)) + ".csv"
			}
			seedVaultFile(t, dir, path, "text/csv", "data", "raw", "keyword alpha beta", 100)
		}

		hits, err := VaultSearch(dir, "keyword", 0)
		if err != nil {
			t.Fatalf("VaultSearch: %v", err)
		}
		if len(hits) != 50 {
			t.Errorf("got %d hits, want 50 (default limit)", len(hits))
		}
	})
}
