package core

import (
	"database/sql"
	"testing"

	_ "github.com/mattn/go-sqlite3"
)

func TestDeleteSourceCascade(t *testing.T) {
	db := EphemeralDb(t)

	if err := AddSource(db, "source1"); err != nil {
		t.Fatalf("failed to add source1: %v", err)
	}
	if err := AddSource(db, "source2"); err != nil {
		t.Fatalf("failed to add source2: %v", err)
	}
	if err := AddItems(db, []Item{
		{"source1", "item1", 0, true, "", "", "", "", 0, nil},
		{"source2", "item2", 0, true, "", "", "", "", 0, nil},
	}); err != nil {
		t.Fatalf("failed to add items: %v", err)
	}

	items, err := GetAllActiveItems(db)
	if err != nil {
		t.Fatalf("failed to get active items: %v", err)
	}
	if len(items) != 2 {
		t.Fatal("Expected 2 items")
	}

	if err := DeleteSource(db, "source1"); err != nil {
		t.Fatal(err)
	}
	items, err = GetAllActiveItems(db)
	if err != nil {
		t.Fatal(err)
	}
	if len(items) != 1 {
		t.Fatalf("Expected only 1 item after source delete, got %d", len(items))
	}

	err = AddItems(db, []Item{{"source1", "item3", 0, true, "", "", "", "", 0, nil}})
	if err == nil {
		t.Fatal("Unexpected success adding item for nonexistent source")
	}
}

func TestTransaction(t *testing.T) {
	db := EphemeralDb(t)
	if _, err := db.Exec("create table planets (name text) strict"); err != nil {
		t.Fatal(err)
	}

	// A transaction that should succeed
	err := db.Transact(func(tx *sql.Tx) error {
		if _, err := tx.Exec("insert into planets (name) values (?)", "mercury"); err != nil {
			t.Fatal(err)
		}
		if _, err := tx.Exec("insert into planets (name) values (?)", "venus"); err != nil {
			t.Fatal(err)
		}
		return nil
	})
	if err != nil {
		t.Fatal(err)
	}

	// Check both rows were inserted
	rows, err := db.Query("select name from planets")
	if err != nil {
		t.Fatal(err)
	}
	found := map[string]bool{}
	for rows.Next() {
		var name string
		if err = rows.Scan(&name); err != nil {
			t.Fatal(err)
		}
		found[name] = true
	}
	if !found["mercury"] || !found["venus"] {
		t.Fatal("transaction failed to insert rows")
	}

	// A transaction that should fail
	err = db.Transact(func(tx *sql.Tx) error {
		if _, err := tx.Exec("insert into planets (name) values (?)", "earth"); err != nil {
			t.Fatal(err)
		}
		_, err := tx.Exec("insert into planets (name) values (?, ?)", "moon", "surprise asteroid!")
		return err
	})
	if err == nil {
		t.Fatal("expected error")
	}

	// Check the third insert was rolled back by the error
	rows, err = db.Query("select name from planets")
	if err != nil {
		t.Fatal(err)
	}
	found = map[string]bool{}
	for rows.Next() {
		var name string
		if err = rows.Scan(&name); err != nil {
			t.Fatal(err)
		}
		found[name] = true
	}
	if found["earth"] {
		t.Fatal("transaction failed to roll back insert")
	}
}