package store

import (
	"context"
	"fmt"
	"net/url"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"

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

func TestRateLimitPolicyModeMigrationClassifiesExistingRows(t *testing.T) {
	databaseURL := strings.TrimSpace(os.Getenv("AI_GATEWAY_TEST_DATABASE_URL"))
	if databaseURL == "" {
		t.Skip("set AI_GATEWAY_TEST_DATABASE_URL to run rate-limit migration PostgreSQL integration tests")
	}
	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
	defer cancel()
	schema := fmt.Sprintf("rate_limit_mode_%d", time.Now().UnixNano())
	adminPool, err := pgxpool.New(ctx, databaseURL)
	if err != nil {
		t.Fatalf("connect migration test database: %v", err)
	}
	defer adminPool.Close()
	if _, err := adminPool.Exec(ctx, `CREATE SCHEMA `+schema); err != nil {
		t.Fatalf("create migration test schema: %v", err)
	}
	defer adminPool.Exec(context.Background(), `DROP SCHEMA IF EXISTS `+schema+` CASCADE`)

	schemaURL, err := url.Parse(databaseURL)
	if err != nil {
		t.Fatalf("parse test database url: %v", err)
	}
	query := schemaURL.Query()
	query.Set("search_path", schema)
	schemaURL.RawQuery = query.Encode()
	pool, err := pgxpool.New(ctx, schemaURL.String())
	if err != nil {
		t.Fatalf("connect migration test schema: %v", err)
	}
	defer pool.Close()
	if _, err := pool.Exec(ctx, `
CREATE TABLE base_model_catalog (
  id uuid PRIMARY KEY,
  default_rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  runtime_policy_set_id uuid,
  runtime_policy_override jsonb NOT NULL DEFAULT '{}'::jsonb
);
CREATE TABLE integration_platforms (
  id uuid PRIMARY KEY,
  rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  updated_at timestamptz NOT NULL DEFAULT now()
);
CREATE TABLE platform_models (
  id uuid PRIMARY KEY,
  base_model_id uuid,
  rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  runtime_policy_set_id uuid,
  runtime_policy_override jsonb NOT NULL DEFAULT '{}'::jsonb,
  updated_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO integration_platforms (id, rate_limit_policy)
VALUES ('20000000-0000-0000-0000-000000000001', '{"rules":[]}');
INSERT INTO base_model_catalog (id, default_rate_limit_policy)
VALUES ('00000000-0000-0000-0000-000000000001', '{"rules":[{"metric":"concurrent","limit":4}]}');
INSERT INTO platform_models (id, base_model_id, rate_limit_policy, runtime_policy_override) VALUES
  ('10000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000001', '{"rules":[{"metric":"concurrent","limit":4}]}', '{}'),
  ('10000000-0000-0000-0000-000000000002', '00000000-0000-0000-0000-000000000001', '{"rules":[{"metric":"concurrent","limit":8}]}', '{}'),
  ('10000000-0000-0000-0000-000000000003', '00000000-0000-0000-0000-000000000001', '{"rules":[{"metric":"concurrent","limit":4}]}', '{"rateLimitPolicy":{"rules":[]}}'),
  ('10000000-0000-0000-0000-000000000004', NULL, '{}', '{}');`); err != nil {
		t.Fatalf("seed pre-migration rows: %v", err)
	}
	_, currentFile, _, _ := runtime.Caller(0)
	migrationPath := filepath.Join(filepath.Dir(currentFile), "..", "..", "migrations", "0080_platform_model_rate_limit_policy_mode.sql")
	migration, err := os.ReadFile(migrationPath)
	if err != nil {
		t.Fatalf("read migration: %v", err)
	}
	if _, err := pool.Exec(ctx, string(migration)); err != nil {
		t.Fatalf("apply migration: %v", err)
	}
	rows, err := pool.Query(ctx, `SELECT id::text, rate_limit_policy_mode FROM platform_models ORDER BY id`)
	if err != nil {
		t.Fatalf("read classified rows: %v", err)
	}
	defer rows.Close()
	want := []string{"inherit", "override", "override", "inherit"}
	index := 0
	for rows.Next() {
		var id, mode string
		if err := rows.Scan(&id, &mode); err != nil {
			t.Fatalf("scan classified row: %v", err)
		}
		if index >= len(want) || mode != want[index] {
			t.Fatalf("row %s mode=%s, want=%s", id, mode, want[index])
		}
		index++
	}
	if err := rows.Err(); err != nil {
		t.Fatalf("read classified rows: %v", err)
	}
	if index != len(want) {
		t.Fatalf("classified %d rows, want %d", index, len(want))
	}
	var normalizedPlatformPolicy string
	if err := pool.QueryRow(ctx, `
SELECT rate_limit_policy::text
FROM integration_platforms
WHERE id = '20000000-0000-0000-0000-000000000001'`).Scan(&normalizedPlatformPolicy); err != nil {
		t.Fatalf("read normalized platform policy: %v", err)
	}
	if normalizedPlatformPolicy != "{}" {
		t.Fatalf("normalized platform policy=%s, want={}", normalizedPlatformPolicy)
	}
}

func TestRemoveUnconfiguredGPTImageConcurrencyLimitMigration(t *testing.T) {
	databaseURL := strings.TrimSpace(os.Getenv("AI_GATEWAY_TEST_DATABASE_URL"))
	if databaseURL == "" {
		t.Skip("set AI_GATEWAY_TEST_DATABASE_URL to run rate-limit migration PostgreSQL integration tests")
	}
	ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
	defer cancel()
	schema := fmt.Sprintf("remove_gpt_image_limit_%d", time.Now().UnixNano())
	adminPool, err := pgxpool.New(ctx, databaseURL)
	if err != nil {
		t.Fatalf("connect migration test database: %v", err)
	}
	defer adminPool.Close()
	if _, err := adminPool.Exec(ctx, `CREATE SCHEMA `+schema); err != nil {
		t.Fatalf("create migration test schema: %v", err)
	}
	defer adminPool.Exec(context.Background(), `DROP SCHEMA IF EXISTS `+schema+` CASCADE`)

	schemaURL, err := url.Parse(databaseURL)
	if err != nil {
		t.Fatalf("parse test database url: %v", err)
	}
	query := schemaURL.Query()
	query.Set("search_path", schema)
	schemaURL.RawQuery = query.Encode()
	pool, err := pgxpool.New(ctx, schemaURL.String())
	if err != nil {
		t.Fatalf("connect migration test schema: %v", err)
	}
	defer pool.Close()
	if _, err := pool.Exec(ctx, `
CREATE TABLE base_model_catalog (
  id uuid PRIMARY KEY,
  invocation_name text NOT NULL,
  default_rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  updated_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO base_model_catalog (id, invocation_name, default_rate_limit_policy, metadata) VALUES
  (
    '00000000-0000-0000-0000-000000000001',
    'gpt-image-2',
    '{"rules":[{"metric":"concurrent","limit":5,"leaseTtlSeconds":300}]}',
    '{"rateLimitSource":"image-gateway-migration","rateLimitUpdatedAt":"2026-07-24"}'
  ),
  (
    '00000000-0000-0000-0000-000000000002',
    'gpt-image-2',
    '{"rules":[{"metric":"concurrent","limit":6,"leaseTtlSeconds":300}]}',
    '{"rateLimitSource":"image-gateway-migration"}'
  ),
  (
    '00000000-0000-0000-0000-000000000003',
    'gpt-image-2',
    '{"rules":[{"metric":"concurrent","limit":5,"leaseTtlSeconds":300}]}',
    '{"rateLimitSource":"operator"}'
  ),
  (
    '00000000-0000-0000-0000-000000000004',
    'gemini-3-pro-image',
    '{"rules":[{"metric":"concurrent","limit":10,"leaseTtlSeconds":600}]}',
    '{"rateLimitSource":"image-gateway-migration"}'
  );`); err != nil {
		t.Fatalf("seed pre-migration rows: %v", err)
	}

	_, currentFile, _, _ := runtime.Caller(0)
	migrationPath := filepath.Join(filepath.Dir(currentFile), "..", "..", "migrations", "0082_remove_unconfigured_gpt_image_concurrency_limit.sql")
	migration, err := os.ReadFile(migrationPath)
	if err != nil {
		t.Fatalf("read migration: %v", err)
	}
	if _, err := pool.Exec(ctx, string(migration)); err != nil {
		t.Fatalf("apply migration: %v", err)
	}

	rows, err := pool.Query(ctx, `
SELECT id::text, default_rate_limit_policy::text, metadata->>'rateLimitSource',
       metadata->>'rateLimitRemovalReason'
FROM base_model_catalog
ORDER BY id`)
	if err != nil {
		t.Fatalf("read migrated rows: %v", err)
	}
	defer rows.Close()
	type result struct {
		policy        string
		source        *string
		removalReason *string
	}
	got := make([]result, 0, 4)
	for rows.Next() {
		var id string
		var item result
		if err := rows.Scan(&id, &item.policy, &item.source, &item.removalReason); err != nil {
			t.Fatalf("scan migrated row: %v", err)
		}
		got = append(got, item)
	}
	if err := rows.Err(); err != nil {
		t.Fatalf("read migrated rows: %v", err)
	}
	if len(got) != 4 {
		t.Fatalf("migrated %d rows, want 4", len(got))
	}
	if got[0].policy != "{}" || got[0].source != nil ||
		got[0].removalReason == nil || *got[0].removalReason != "upstream-limit-unconfigured" {
		t.Fatalf("migration-owned GPT policy not removed: %+v", got[0])
	}
	if got[1].policy == "{}" || got[1].source == nil || *got[1].source != "image-gateway-migration" {
		t.Fatalf("custom GPT policy was changed: %+v", got[1])
	}
	if got[2].policy == "{}" || got[2].source == nil || *got[2].source != "operator" {
		t.Fatalf("operator GPT policy was changed: %+v", got[2])
	}
	if got[3].policy == "{}" || got[3].source == nil || *got[3].source != "image-gateway-migration" {
		t.Fatalf("Gemini policy was changed: %+v", got[3])
	}
}
