package persistence

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

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

func TestIntegration_PermissionStateRestoreFinalizerReplacesAndFencesLineage(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)
			}
		}
	}

	stateRepository := newPermissionStateRepositoryForTest(t, db, permissionStateLineage)
	if _, err := stateRepository.EnsureInitialized(context.Background()); err != nil {
		t.Fatal(err)
	}
	finalizer := newPermissionStateRestoreFinalizerForTest(t, db)
	initialLineage := permissionStateLineageFromBytes(t, permissionStateLineage)
	restoredLineage := permissionStateLineageFromBytes(t, permissionStateRestoredLineage)
	restored, err := finalizer.FinalizeExternallyFencedRestore(
		context.Background(),
		initialLineage,
		restoredLineage,
	)
	if err != nil {
		t.Fatal(err)
	}
	assertPermissionState(t, restored, permissionStateRestoredLineage, 1, PermissionStateDarkMode)

	modifiedBefore := readPermissionStateModifiedAt(t, db)
	retried, err := finalizer.FinalizeExternallyFencedRestore(
		context.Background(),
		initialLineage,
		restoredLineage,
	)
	if err != nil {
		t.Fatal(err)
	}
	assertPermissionState(t, retried, permissionStateRestoredLineage, 1, PermissionStateDarkMode)
	if modifiedAfter := readPermissionStateModifiedAt(t, db); !modifiedAfter.Equal(modifiedBefore) {
		t.Fatalf("exact retry changed LastModifiedDate from %v to %v", modifiedBefore, modifiedAfter)
	}

	transactions := NewTransactionManager(database.NewConnectionFromDB(db))
	authority := newPermissionStateAuthorityForTest(t, transactions)
	if err := transactions.WithPessimisticTransaction(context.Background(), func(ctx context.Context) error {
		_, _, err := authority.AdvanceIfChanged(ctx, true)
		return err
	}); err != nil {
		t.Fatal(err)
	}
	if _, err := finalizer.FinalizeExternallyFencedRestore(
		context.Background(),
		initialLineage,
		restoredLineage,
	); err == nil {
		t.Fatal("finalizer reused a lineage after revision advance")
	}
	advanced, err := stateRepository.CheckReady(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	assertPermissionState(t, advanced, permissionStateRestoredLineage, 2, PermissionStateDarkMode)

	thirdLineage := permissionStateLineageFromBytes(t, permissionStateNextRestoredLineage)
	third, err := finalizer.FinalizeExternallyFencedRestore(
		context.Background(),
		restoredLineage,
		thirdLineage,
	)
	if err != nil {
		t.Fatal(err)
	}
	assertPermissionState(t, third, permissionStateNextRestoredLineage, 1, PermissionStateDarkMode)

	if _, err := finalizer.FinalizeExternallyFencedRestore(
		context.Background(),
		initialLineage,
		restoredLineage,
	); err == nil {
		t.Fatal("stale finalizer request replaced a later lineage")
	}
	current, err := stateRepository.CheckReady(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	assertPermissionState(t, current, permissionStateNextRestoredLineage, 1, PermissionStateDarkMode)
}

func readPermissionStateModifiedAt(t *testing.T, db *sql.DB) time.Time {
	t.Helper()
	var modified time.Time
	if err := db.QueryRow(
		"SELECT LastModifiedDate FROM _System_PermissionStateVersion WHERE SingletonKey = 1",
	).Scan(&modified); err != nil {
		t.Fatal(err)
	}
	return modified
}
