package httpapi

import (
	"context"
	"testing"
	"time"

	"github.com/jackc/pgx/v5/pgxpool"
)

type acceptanceStatusSnapshot struct {
	ID     string
	Status string
}

type acceptanceUserGroupSnapshot struct {
	ID              string
	RateLimitPolicy []byte
}

// isolateHTTPAcceptanceState lets a stress test disable unrelated runtime
// resources without leaking those mutations into later integration tests that
// intentionally share the same dedicated test database.
func isolateHTTPAcceptanceState(
	t *testing.T,
	ctx context.Context,
	pool *pgxpool.Pool,
	includeStorage bool,
) func() {
	t.Helper()
	startedAt := time.Now()
	platforms := readAcceptanceStatusSnapshots(t, ctx, pool, `
SELECT id::text, status
FROM integration_platforms
WHERE deleted_at IS NULL`)
	userGroups := make([]acceptanceUserGroupSnapshot, 0)
	rows, err := pool.Query(ctx, `
SELECT id::text, rate_limit_policy
FROM gateway_user_groups`)
	if err != nil {
		t.Fatalf("snapshot acceptance user groups: %v", err)
	}
	for rows.Next() {
		var item acceptanceUserGroupSnapshot
		if err := rows.Scan(&item.ID, &item.RateLimitPolicy); err != nil {
			rows.Close()
			t.Fatalf("scan acceptance user group snapshot: %v", err)
		}
		userGroups = append(userGroups, item)
	}
	if err := rows.Err(); err != nil {
		rows.Close()
		t.Fatalf("read acceptance user group snapshots: %v", err)
	}
	rows.Close()

	storageChannels := []acceptanceStatusSnapshot(nil)
	if includeStorage {
		storageChannels = readAcceptanceStatusSnapshots(t, ctx, pool, `
SELECT id::text, status
FROM file_storage_channels
WHERE deleted_at IS NULL`)
	}
	if _, err := pool.Exec(ctx, `
UPDATE integration_platforms
SET status = 'disabled'
WHERE deleted_at IS NULL`); err != nil {
		t.Fatalf("isolate acceptance platforms: %v", err)
	}
	if includeStorage {
		if _, err := pool.Exec(ctx, `
UPDATE file_storage_channels
SET status = 'disabled'
WHERE deleted_at IS NULL`); err != nil {
			t.Fatalf("isolate acceptance storage channels: %v", err)
		}
	}

	return func() {
		restoreCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
		defer cancel()
		tx, err := pool.Begin(restoreCtx)
		if err != nil {
			t.Errorf("begin acceptance state restore: %v", err)
			return
		}
		defer func() {
			_ = tx.Rollback(restoreCtx)
		}()
		for _, item := range platforms {
			if _, err := tx.Exec(restoreCtx, `
UPDATE integration_platforms
SET status = $2
WHERE id = $1::uuid`, item.ID, item.Status); err != nil {
				t.Errorf("restore acceptance platform %s: %v", item.ID, err)
				return
			}
		}
		if _, err := tx.Exec(restoreCtx, `
UPDATE integration_platforms
SET status = 'disabled'
WHERE created_at >= $1
  AND deleted_at IS NULL`, startedAt); err != nil {
			t.Errorf("disable acceptance-created platforms: %v", err)
			return
		}
		for _, item := range userGroups {
			if _, err := tx.Exec(restoreCtx, `
UPDATE gateway_user_groups
SET rate_limit_policy = $2::jsonb
WHERE id = $1::uuid`, item.ID, string(item.RateLimitPolicy)); err != nil {
				t.Errorf("restore acceptance user group %s: %v", item.ID, err)
				return
			}
		}
		if includeStorage {
			for _, item := range storageChannels {
				if _, err := tx.Exec(restoreCtx, `
UPDATE file_storage_channels
SET status = $2
WHERE id = $1::uuid`, item.ID, item.Status); err != nil {
					t.Errorf("restore acceptance storage channel %s: %v", item.ID, err)
					return
				}
			}
			if _, err := tx.Exec(restoreCtx, `
UPDATE file_storage_channels
SET status = 'disabled'
WHERE created_at >= $1
  AND deleted_at IS NULL`, startedAt); err != nil {
				t.Errorf("disable acceptance-created storage channels: %v", err)
				return
			}
		}
		if err := tx.Commit(restoreCtx); err != nil {
			t.Errorf("commit acceptance state restore: %v", err)
		}
	}
}

func readAcceptanceStatusSnapshots(
	t *testing.T,
	ctx context.Context,
	pool *pgxpool.Pool,
	query string,
) []acceptanceStatusSnapshot {
	t.Helper()
	rows, err := pool.Query(ctx, query)
	if err != nil {
		t.Fatalf("snapshot acceptance statuses: %v", err)
	}
	defer rows.Close()
	items := make([]acceptanceStatusSnapshot, 0)
	for rows.Next() {
		var item acceptanceStatusSnapshot
		if err := rows.Scan(&item.ID, &item.Status); err != nil {
			t.Fatalf("scan acceptance status snapshot: %v", err)
		}
		items = append(items, item)
	}
	if err := rows.Err(); err != nil {
		t.Fatalf("read acceptance status snapshots: %v", err)
	}
	return items
}
