package store

import (
	"context"
	"sync"
	"testing"
	"time"

	"github.com/easyai/easyai-ai-gateway/apps/api/internal/auth"
	"github.com/jackc/pgx/v5/pgxpool"
)

func TestVerifyLocalAPIKeyWorksWithSingleConnectionPool(t *testing.T) {
	db, verificationStore, created, user := newLocalAPIKeyVerificationFixture(t, 1)
	ctx := context.Background()

	verifyCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
	defer cancel()
	verified, err := verificationStore.VerifyLocalAPIKey(verifyCtx, created.Secret)
	if err != nil {
		t.Fatalf("verify local API key with one connection: %v", err)
	}
	if verified.APIKeyID != created.APIKey.ID || verified.GatewayUserID != user.ID {
		t.Fatalf("verified identity = %+v, want API key %q and user %q", verified, created.APIKey.ID, user.ID)
	}

	var lastUsedAt *time.Time
	if err := db.pool.QueryRow(ctx, `SELECT last_used_at FROM gateway_api_keys WHERE id=$1::uuid`, created.APIKey.ID).Scan(&lastUsedAt); err != nil {
		t.Fatalf("read API key last_used_at: %v", err)
	}
	if lastUsedAt == nil {
		t.Fatal("successful API key verification did not update last_used_at")
	}
}

func TestVerifyLocalAPIKeyHandlesEightConcurrentRequestsWithFourConnections(t *testing.T) {
	_, verificationStore, created, _ := newLocalAPIKeyVerificationFixture(t, 4)
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	const requestCount = 8
	start := make(chan struct{})
	errorsByRequest := make(chan error, requestCount)
	var requests sync.WaitGroup
	requests.Add(requestCount)
	for range requestCount {
		go func() {
			defer requests.Done()
			<-start
			_, err := verificationStore.VerifyLocalAPIKey(ctx, created.Secret)
			errorsByRequest <- err
		}()
	}
	close(start)
	requests.Wait()
	close(errorsByRequest)

	for err := range errorsByRequest {
		if err != nil {
			t.Fatalf("concurrent API key verification failed: %v", err)
		}
	}
	if acquired := verificationStore.pool.Stat().AcquiredConns(); acquired != 0 {
		t.Fatalf("API key verification left %d connections acquired", acquired)
	}
}

func TestVerifyLocalAPIKeyCoalescesUsageUpdates(t *testing.T) {
	db, verificationStore, created, _ := newLocalAPIKeyVerificationFixture(t, 1)
	ctx := context.Background()

	if _, err := verificationStore.VerifyLocalAPIKey(ctx, created.Secret); err != nil {
		t.Fatalf("verify local API key first time: %v", err)
	}
	var firstLastUsedAt time.Time
	if err := db.pool.QueryRow(ctx, `
SELECT last_used_at
FROM gateway_api_keys
WHERE id = $1::uuid`, created.APIKey.ID).Scan(&firstLastUsedAt); err != nil {
		t.Fatalf("read first API key usage timestamp: %v", err)
	}

	if _, err := verificationStore.VerifyLocalAPIKey(ctx, created.Secret); err != nil {
		t.Fatalf("verify local API key second time: %v", err)
	}
	var secondLastUsedAt time.Time
	if err := db.pool.QueryRow(ctx, `
SELECT last_used_at
FROM gateway_api_keys
WHERE id = $1::uuid`, created.APIKey.ID).Scan(&secondLastUsedAt); err != nil {
		t.Fatalf("read second API key usage timestamp: %v", err)
	}
	if !secondLastUsedAt.Equal(firstLastUsedAt) {
		t.Fatalf("usage timestamp changed inside coalescing window: first=%s second=%s", firstLastUsedAt, secondLastUsedAt)
	}
}

func newLocalAPIKeyVerificationFixture(t *testing.T, maxConnections int32) (*Store, *Store, CreatedAPIKey, GatewayUser) {
	t.Helper()
	db := newIdentityPairingPostgresTestStore(t)
	ctx := context.Background()
	user, err := db.RegisterLocalUser(ctx, LocalRegisterInput{
		Username: "api-key-verification-user",
		Password: "api-key-verification-password",
	})
	if err != nil {
		t.Fatalf("register API key verification user: %v", err)
	}
	created, err := db.CreateAPIKey(ctx, CreateAPIKeyInput{Name: "API key verification fixture"}, &auth.User{
		ID:              user.ID,
		GatewayUserID:   user.ID,
		GatewayTenantID: user.GatewayTenantID,
		TenantID:        user.TenantID,
		TenantKey:       user.TenantKey,
	})
	if err != nil {
		t.Fatalf("create API key verification fixture: %v", err)
	}

	config := db.pool.Config()
	config.MaxConns = maxConnections
	config.MinConns = 0
	pool, err := pgxpool.NewWithConfig(ctx, config)
	if err != nil {
		t.Fatalf("create verification pool with %d connections: %v", maxConnections, err)
	}
	t.Cleanup(pool.Close)
	return db, &Store{pool: pool}, created, user
}
