package persistence

import (
	"context"
	"database/sql"
	"errors"
	"testing"

	database "github.com/nexuscrm/db"
	"github.com/nexuscrm/kernel/pkg/schemacatalog"
)

func TestIntegration_PermissionStateAuthoritySharesSemanticTransaction(t *testing.T) {
	db, cleanup := openSchemaDDLTiDB(t)
	defer cleanup()

	definitions, err := schemacatalog.ManagedTableDefinitions()
	if err != nil {
		t.Fatal(err)
	}
	schemaRepository := NewSchemaRepository(db, nil)
	for _, definition := range definitions {
		if definition.TableName == PermissionStateVersionTable {
			if err := schemaRepository.CreatePhysicalTable(context.Background(), definition); err != nil {
				t.Fatalf("CreatePhysicalTable(%s): %v", definition.TableName, err)
			}
		}
	}
	if _, err := db.Exec(`CREATE TABLE PermissionStateAuthoritySemanticProbe (
		Id BIGINT PRIMARY KEY,
		Value VARCHAR(16) NOT NULL
	)`); err != nil {
		t.Fatal(err)
	}

	stateRepository := newPermissionStateRepositoryForTest(t, db, permissionStateLineage)
	initial, err := stateRepository.EnsureInitialized(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	assertPermissionState(t, initial, permissionStateLineage, 1, PermissionStateDarkMode)

	transactions := NewTransactionManager(database.NewConnectionFromDB(db))
	authority := newPermissionStateAuthorityForTest(t, transactions)
	err = transactions.WithPessimisticTransaction(context.Background(), func(ctx context.Context) error {
		first, advanced, err := authority.AdvanceIfChanged(ctx, true)
		if err != nil || !advanced {
			t.Fatalf("first AdvanceIfChanged() = advanced %t, error %v", advanced, err)
		}
		second, advanced, err := authority.AdvanceIfChanged(ctx, true)
		if err != nil || !advanced {
			t.Fatalf("nested AdvanceIfChanged() = advanced %t, error %v", advanced, err)
		}
		assertPermissionStateReceiptEqual(t, second, first)
		tx := transactions.ExtractTx(ctx).(*sql.Tx)
		_, err = tx.ExecContext(ctx, "INSERT INTO PermissionStateAuthoritySemanticProbe (Id, Value) VALUES (1, 'committed')")
		return err
	})
	if err != nil {
		t.Fatalf("committed semantic transaction: %v", err)
	}
	committed, err := stateRepository.CheckReady(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	assertPermissionState(t, committed, permissionStateLineage, 2, PermissionStateDarkMode)
	assertCount(t, db, "SELECT COUNT(*) FROM PermissionStateAuthoritySemanticProbe WHERE Id = 1", 1, "semantic write must commit with revision")

	semanticFailure := errors.New("semantic write failed")
	err = transactions.WithPessimisticTransaction(context.Background(), func(ctx context.Context) error {
		if _, advanced, err := authority.AdvanceIfChanged(ctx, true); err != nil || !advanced {
			t.Fatalf("rollback AdvanceIfChanged() = advanced %t, error %v", advanced, err)
		}
		tx := transactions.ExtractTx(ctx).(*sql.Tx)
		if _, err := tx.ExecContext(ctx, "INSERT INTO PermissionStateAuthoritySemanticProbe (Id, Value) VALUES (2, 'rolled-back')"); err != nil {
			return err
		}
		return semanticFailure
	})
	if !errors.Is(err, semanticFailure) {
		t.Fatalf("rolled-back semantic transaction error = %v, want %v", err, semanticFailure)
	}
	rolledBack, err := stateRepository.CheckReady(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	assertPermissionState(t, rolledBack, permissionStateLineage, 2, PermissionStateDarkMode)
	assertCount(t, db, "SELECT COUNT(*) FROM PermissionStateAuthoritySemanticProbe WHERE Id = 2", 0, "semantic write and revision must roll back together")

	err = transactions.WithPessimisticTransaction(context.Background(), func(ctx context.Context) error {
		_, advanced, err := authority.AdvanceIfChanged(ctx, false)
		if advanced {
			t.Fatal("semantic no-op advanced Permission State")
		}
		return err
	})
	if err != nil {
		t.Fatalf("no-op semantic transaction: %v", err)
	}
	noOp, err := stateRepository.CheckReady(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	assertPermissionState(t, noOp, permissionStateLineage, 2, PermissionStateDarkMode)
}
