package persistence

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

	database "github.com/nexuscrm/db"
)

func TestLoadAuthorizingSessionStateRejectsRevocationCommittedAfterCallerSnapshotOnTiDB(t *testing.T) {
	db, cleanup := openSessionRepositoryTiDB(t)
	defer cleanup()

	for _, statement := range []string{
		`CREATE TABLE User (
			Id VARCHAR(64) PRIMARY KEY,
			ProfileId VARCHAR(64) NOT NULL,
			UserRoleId VARCHAR(64) NULL,
			IsActive BOOL NOT NULL,
			IsDeleted BOOL NOT NULL
		)`,
		`CREATE TABLE _System_Session (
			Id VARCHAR(64) PRIMARY KEY,
			UserId VARCHAR(64) NOT NULL,
			Token VARBINARY(3000) NOT NULL,
			CurrentBearerToken VARBINARY(3000) NULL,
			ExpiresAt DATETIME(6) NOT NULL,
			IsRevoked BOOL NOT NULL,
			AuthEpoch DECIMAL(20,0) NOT NULL,
			ControlProofHash CHAR(64) NULL,
			LastActivity DATETIME(6) NOT NULL,
			Scope TEXT NULL,
			ConnectedAppId VARCHAR(64) NULL,
			GrantType VARCHAR(50) NULL
		)`,
		`CREATE TABLE _System_ConnectedApp (
			Id VARCHAR(64) PRIMARY KEY,
			IsActive BOOL NOT NULL
		)`,
		`CREATE TABLE _System_ConnectedAppDeclaration (
			ConnectedAppId VARCHAR(64) NOT NULL,
			InstalledPackName VARCHAR(255) NULL,
			IsDeleted BOOL NOT NULL
		)`,
		`CREATE TABLE _System_OAuthDynamicClientRegistration (
			ConnectedAppId VARCHAR(64) NOT NULL,
			ScopeOwnerPack VARCHAR(255) NULL,
			IsDeleted BOOL NOT NULL
		)`,
		`CREATE TABLE _System_PermissionStateVersion (
			SingletonKey BIGINT PRIMARY KEY,
			Lineage BINARY(16) NOT NULL,
			Revision DECIMAL(20,0) NOT NULL,
			EnforcementMode VARCHAR(32) NOT NULL,
			CreatedDate DATETIME(6) NOT NULL,
			LastModifiedDate DATETIME(6) NOT NULL
		)`,
	} {
		if _, err := db.Exec(statement); err != nil {
			t.Fatalf("create authorizing-session fixture: %v", err)
		}
	}

	now := time.Now().UTC().Truncate(time.Second)
	if _, err := db.Exec(
		`INSERT INTO User (Id, ProfileId, UserRoleId, IsActive, IsDeleted)
		 VALUES ('user-1', 'profile-1', NULL, 1, 0)`,
	); err != nil {
		t.Fatal(err)
	}
	if _, err := db.Exec(
		`INSERT INTO _System_Session
		 (Id, UserId, Token, CurrentBearerToken, ExpiresAt, IsRevoked, AuthEpoch,
		  LastActivity, Scope, ConnectedAppId, GrantType)
		 VALUES (?, 'user-1', ?, ?, ?, 0, 7, ?, 'api', NULL, 'password')`,
		"session-1", sessionTokenVerifier("bearer"), "bearer", now.Add(time.Hour), now,
	); err != nil {
		t.Fatal(err)
	}
	if _, err := db.Exec(
		`INSERT INTO _System_PermissionStateVersion
		 (SingletonKey, Lineage, Revision, EnforcementMode, CreatedDate, LastModifiedDate)
		 VALUES (1, ?, 19, 'dark', ?, ?)`,
		permissionStateLineage, now, now,
	); err != nil {
		t.Fatal(err)
	}

	transactions := NewTransactionManager(database.NewConnectionFromDB(db))
	repository := NewSessionRepository(db)
	err := transactions.WithPessimisticTransaction(
		context.Background(),
		func(txCtx context.Context) error {
			tx, ok := transactions.ExtractTx(txCtx).(*sql.Tx)
			if !ok {
				return fmt.Errorf("transaction executor = %T, want *sql.Tx", transactions.ExtractTx(txCtx))
			}
			var snapshotEpoch uint64
			if err := tx.QueryRowContext(
				txCtx,
				"SELECT AuthEpoch FROM _System_Session WHERE Id = ?",
				"session-1",
			).Scan(&snapshotEpoch); err != nil {
				return err
			}
			if snapshotEpoch != 7 {
				return fmt.Errorf("snapshot AuthEpoch = %d, want 7", snapshotEpoch)
			}

			if _, err := db.ExecContext(
				txCtx,
				"UPDATE _System_Session SET IsRevoked = 1, AuthEpoch = 8 WHERE Id = ?",
				"session-1",
			); err != nil {
				return err
			}
			state, err := repository.LoadAuthorizingSessionState(txCtx, "session-1")
			if err != nil {
				return err
			}
			if state != nil {
				return fmt.Errorf("LoadAuthorizingSessionState = %#v, want fenced denial", state)
			}
			return nil
		},
	)
	if err != nil {
		t.Fatalf("WithPessimisticTransaction: %v", err)
	}
}
