package infra

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

// setupAggregateTestRepo creates a minimal repo layout:
//
//	<root>/
//	  registry.db  (SQLite, empty)
//	  projects/<project>/vaults/vault.yaml
//	  <vaultPath>/    (optionally with vault_index.db populated)
func setupAggregateTestRepo(t *testing.T, vaultName, projectID, vaultPath string, withIndex bool) string {
	t.Helper()
	root := t.TempDir()

	// Create registry.db
	regDB, err := SQLiteOpen(filepath.Join(root, "registry.db"), "")
	if err != nil {
		t.Fatalf("create registry.db: %v", err)
	}
	regDB.Close()

	// Create project vault manifest
	projVaultsDir := filepath.Join(root, "projects", projectID, "vaults")
	if err := os.MkdirAll(projVaultsDir, 0755); err != nil {
		t.Fatalf("mkdir projects: %v", err)
	}
	manifestYAML := "vaults:\n  - name: " + vaultName + "\n    description: test\n    path: " + vaultPath + "\n    tags: []\n"
	if err := os.WriteFile(filepath.Join(projVaultsDir, "vault.yaml"), []byte(manifestYAML), 0644); err != nil {
		t.Fatalf("write vault.yaml: %v", err)
	}

	// Create vault dir
	if err := os.MkdirAll(vaultPath, 0755); err != nil {
		t.Fatalf("mkdir vault: %v", err)
	}

	if withIndex {
		// Create a vault_index.db with one file row
		vdb, err := VaultIndexOpen(vaultPath)
		if err != nil {
			t.Fatalf("VaultIndexOpen: %v", err)
		}
		now := time.Now().UTC().Unix()
		_, err = vdb.Exec(`INSERT INTO files (rel_path, size, mtime, sha256, mime, ext, bucket, sub_bucket, indexed_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
			"data/raw/sample.csv", 1024, now, "deadbeef", "text/csv", ".csv", "data", "raw", now)
		if err != nil {
			t.Fatalf("insert test file: %v", err)
		}
		vdb.Close()
	}

	return root
}

func TestVaultAggregateIndex_NoVaults(t *testing.T) {
	root := t.TempDir()
	// No manifests, just registry.db
	regDB, err := SQLiteOpen(filepath.Join(root, "registry.db"), "")
	if err != nil {
		t.Fatalf("create registry.db: %v", err)
	}
	regDB.Close()

	report, err := VaultAggregateIndex(root)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if report.VaultsProcessed != 0 {
		t.Errorf("VaultsProcessed: want 0, got %d", report.VaultsProcessed)
	}
	if len(report.Errors) != 0 {
		t.Errorf("Errors: want empty, got %v", report.Errors)
	}
}

func TestVaultAggregateIndex_VaultWithoutIndex(t *testing.T) {
	vaultDir := t.TempDir()
	root := setupAggregateTestRepo(t, "my_vault", "my_proj", vaultDir, false /* no vault_index.db */)

	report, err := VaultAggregateIndex(root)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if report.VaultsSkipped != 1 {
		t.Errorf("VaultsSkipped: want 1, got %d", report.VaultsSkipped)
	}
	if report.VaultsProcessed != 0 {
		t.Errorf("VaultsProcessed: want 0, got %d", report.VaultsProcessed)
	}
}

func TestVaultAggregateIndex_HappyPath(t *testing.T) {
	vaultDir := t.TempDir()
	root := setupAggregateTestRepo(t, "my_vault", "my_proj", vaultDir, true)

	report, err := VaultAggregateIndex(root)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if report.VaultsProcessed != 1 {
		t.Errorf("VaultsProcessed: want 1, got %d", report.VaultsProcessed)
	}
	if report.TotalFiles != 1 {
		t.Errorf("TotalFiles: want 1, got %d", report.TotalFiles)
	}

	// Verify row exists in registry.db
	regDB, err := SQLiteOpen(filepath.Join(root, "registry.db"), "")
	if err != nil {
		t.Fatalf("open registry.db: %v", err)
	}
	defer regDB.Close()

	var count int
	if err := regDB.QueryRow(`SELECT COUNT(*) FROM vault_files`).Scan(&count); err != nil {
		t.Fatalf("count vault_files: %v", err)
	}
	if count != 1 {
		t.Errorf("vault_files count: want 1, got %d", count)
	}
}

func TestVaultAggregateIndex_ReRunReplaces(t *testing.T) {
	vaultDir := t.TempDir()
	root := setupAggregateTestRepo(t, "my_vault", "my_proj", vaultDir, true)

	// First run
	if _, err := VaultAggregateIndex(root); err != nil {
		t.Fatalf("first run: %v", err)
	}

	// Add a second file to vault_index.db
	vdb, err := VaultIndexOpen(vaultDir)
	if err != nil {
		t.Fatalf("reopen vault index: %v", err)
	}
	now := time.Now().UTC().Unix()
	_, err = vdb.Exec(`INSERT INTO files (rel_path, size, mtime, sha256, mime, ext, bucket, sub_bucket, indexed_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		"data/raw/extra.csv", 512, now, "cafebabe", "text/csv", ".csv", "data", "raw", now)
	if err != nil {
		t.Fatalf("insert second file: %v", err)
	}
	vdb.Close()

	// Second run
	report, err := VaultAggregateIndex(root)
	if err != nil {
		t.Fatalf("second run: %v", err)
	}
	if report.TotalFiles != 2 {
		t.Errorf("TotalFiles: want 2, got %d", report.TotalFiles)
	}

	// Verify no duplicates — exactly 2 rows
	regDB, err := SQLiteOpen(filepath.Join(root, "registry.db"), "")
	if err != nil {
		t.Fatalf("open registry.db: %v", err)
	}
	defer regDB.Close()

	var count int
	if err := regDB.QueryRow(`SELECT COUNT(*) FROM vault_files`).Scan(&count); err != nil {
		t.Fatalf("count vault_files: %v", err)
	}
	if count != 2 {
		t.Errorf("vault_files count after re-run: want 2, got %d", count)
	}
}
