package database

import (
	"bufio"
	"bytes"
	"context"
	"os"
	"sort"
	"testing"

	"lol.mleku.dev/chk"
	"next.orly.dev/pkg/encoders/event"
	"next.orly.dev/pkg/encoders/event/examples"
)

// TestExport tests the Export function by:
// 1. Creating a new database with events from examples.Cache
// 2. Checking that all event IDs in the cache are found in the export
// 3. Verifying this also works when only a few pubkeys are requested
func TestExport(t *testing.T) {
	// Create a temporary directory for the database
	tempDir, err := os.MkdirTemp("", "test-db-*")
	if err != nil {
		t.Fatalf("Failed to create temporary directory: %v", err)
	}
	defer os.RemoveAll(tempDir) // Clean up after the test

	// Create a context and cancel function for the database
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Initialize the database
	db, err := New(ctx, cancel, tempDir, "info")
	if err != nil {
		t.Fatalf("Failed to create database: %v", err)
	}
	defer db.Close()

	// Create a scanner to read events from examples.Cache
	scanner := bufio.NewScanner(bytes.NewBuffer(examples.Cache))
	scanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)

	var events []*event.E

	// First, collect all events
	for scanner.Scan() {
		chk.E(scanner.Err())
		b := scanner.Bytes()
		ev := event.New()

		// Unmarshal the event
		if _, err = ev.Unmarshal(b); chk.E(err) {
			t.Fatal(err)
		}

		events = append(events, ev)
	}

	// Check for scanner errors
	if err = scanner.Err(); err != nil {
		t.Fatalf("Scanner error: %v", err)
	}

	// Sort events by CreatedAt to ensure addressable events are processed in chronological order
	sort.Slice(events, func(i, j int) bool {
		return events[i].CreatedAt < events[j].CreatedAt
	})

	// Maps to store event IDs and their associated pubkeys
	eventIDs := make(map[string]bool)
	pubkeyToEventIDs := make(map[string][]string)

	// Process each event in chronological order
	for _, ev := range events {
		// Save the event to the database
		if _, err = db.SaveEvent(ctx, ev); err != nil {
			t.Fatalf("Failed to save event: %v", err)
		}

		// Store the event ID
		eventID := string(ev.ID)
		eventIDs[eventID] = true

		// Store the event ID by pubkey
		pubkey := string(ev.Pubkey)
		pubkeyToEventIDs[pubkey] = append(pubkeyToEventIDs[pubkey], eventID)
	}

	t.Logf("Saved %d events to the database", len(eventIDs))

	// Test 1: Export all events and verify all IDs are in the export
	var exportBuffer bytes.Buffer
	db.Export(ctx, &exportBuffer)

	// Parse the exported events and check that all IDs are present
	exportedIDs := make(map[string]bool)
	exportScanner := bufio.NewScanner(&exportBuffer)
	exportScanner.Buffer(make([]byte, 0, 1_000_000_000), 1_000_000_000)
	exportCount := 0
	for exportScanner.Scan() {
		b := exportScanner.Bytes()
		ev := event.New()
		if _, err = ev.Unmarshal(b); chk.E(err) {
			t.Fatal(err)
		}
		exportedIDs[string(ev.ID)] = true
		exportCount++
	}
	// Check for scanner errors
	if err = exportScanner.Err(); err != nil {
		t.Fatalf("Scanner error: %v", err)
	}

	t.Logf("Found %d events in the export", exportCount)

	// todo: this fails because some of the events replace earlier versions
	// // Check that all original event IDs are in the export
	// for id := range eventIDs {
	// 	if !exportedIDs[id] {
	// 		t.Errorf("Event ID %0x not found in export", id)
	// 	}
	// }

	t.Logf("All %d event IDs found in export", len(eventIDs))
}
