package persistence

import (
	"context"
	"fmt"
	"testing"
	"time"

	database "github.com/nexuscrm/db"
)

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

	for _, statement := range []string{
		`CREATE TABLE _System_ConnectedApp (
			Id VARCHAR(64) PRIMARY KEY
		)`,
		`CREATE TABLE _System_ConnectedAppAccess (
			Id VARCHAR(64) PRIMARY KEY,
			ConnectedAppId VARCHAR(64) NOT NULL,
			INDEX idx_connected_app_access (ConnectedAppId)
		)`,
		`INSERT INTO _System_ConnectedApp (Id) VALUES ('app-1')`,
		`INSERT INTO _System_ConnectedAppAccess (Id, ConnectedAppId) VALUES ('access-1', 'app-1')`,
	} {
		if _, err := db.Exec(statement); err != nil {
			t.Fatalf("prepare Connected App fixture: %v", err)
		}
	}

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	revokeTx, revokeCleanup, err := BeginPessimisticTx(ctx, db)
	if err != nil {
		t.Fatalf("begin revocation transaction: %v", err)
	}
	revokeFinished := false
	defer func() {
		if !revokeFinished {
			_ = revokeTx.Rollback()
			_ = revokeCleanup()
		}
	}()
	var lockedAppID string
	if err := revokeTx.QueryRowContext(
		ctx,
		"SELECT Id FROM _System_ConnectedApp WHERE Id = ? FOR UPDATE",
		"app-1",
	).Scan(&lockedAppID); err != nil {
		t.Fatalf("lock Connected App for revocation: %v", err)
	}
	if _, err := revokeTx.ExecContext(
		ctx,
		"DELETE FROM _System_ConnectedAppAccess WHERE ConnectedAppId = ?",
		"app-1",
	); err != nil {
		t.Fatalf("delete Connected App access: %v", err)
	}

	repo := NewSystemRepository(db)
	if err := repo.SetTransactionManager(NewTransactionManager(database.NewConnectionFromDB(db))); err != nil {
		t.Fatalf("SetTransactionManager: %v", err)
	}
	issuanceDone := make(chan error, 1)
	go func() {
		issuanceDone <- repo.WithConnectedAppCredentialIssuance(ctx, "app-1", func(txCtx context.Context) error {
			var accessRows int
			if err := repo.GetExecutor(txCtx).QueryRowContext(
				txCtx,
				"SELECT COUNT(*) FROM _System_ConnectedAppAccess WHERE ConnectedAppId = ?",
				"app-1",
			).Scan(&accessRows); err != nil {
				return err
			}
			if accessRows != 0 {
				return fmt.Errorf("credential issuance observed %d stale access row(s) after revocation", accessRows)
			}
			return nil
		})
	}()

	select {
	case err := <-issuanceDone:
		t.Fatalf("credential issuance completed before revocation released its lock: %v", err)
	case <-time.After(250 * time.Millisecond):
	}

	if err := revokeTx.Commit(); err != nil {
		t.Fatalf("commit Connected App revocation: %v", err)
	}
	if err := revokeCleanup(); err != nil {
		t.Fatalf("clean up revocation transaction: %v", err)
	}
	revokeFinished = true

	select {
	case err := <-issuanceDone:
		if err != nil {
			t.Fatalf("credential issuance after revocation: %v", err)
		}
	case <-ctx.Done():
		t.Fatalf("credential issuance did not resume after revocation committed: %v", ctx.Err())
	}

	var accessRows int
	if err := db.QueryRowContext(
		context.Background(),
		"SELECT COUNT(*) FROM _System_ConnectedAppAccess WHERE ConnectedAppId = ?",
		"app-1",
	).Scan(&accessRows); err != nil {
		t.Fatalf("read final Connected App access: %v", err)
	}
	if accessRows != 0 {
		t.Fatalf("final Connected App access rows = %d, want 0", accessRows)
	}
}
