WITH defaults AS (
  SELECT
    '[
      {
        "enabled": true,
        "categories": ["rate_limit"],
        "codes": ["rate_limit"],
        "statusCodes": [429],
        "keywords": ["rate_limit", "429", "too many requests"],
        "action": "cooldown_and_next",
        "target": "model",
        "cooldownSeconds": 300
      },
      {
        "enabled": true,
        "categories": ["auth_error"],
        "codes": ["auth_failed", "invalid_api_key", "missing_credentials"],
        "statusCodes": [401, 403],
        "keywords": ["invalid api key", "api key is invalid", "unauthorized", "forbidden", "permission denied"],
        "action": "disable_and_next",
        "target": "platform"
      },
      {
        "enabled": true,
        "categories": ["network", "timeout", "stream_error", "rate_limit", "provider_5xx", "provider_overloaded"],
        "codes": ["network", "timeout", "stream_read_error", "rate_limit", "server_error", "overloaded"],
        "statusCodes": [408, 429, 500, 502, 503, 504],
        "keywords": ["timeout", "network", "rate_limit", "overloaded", "temporarily_unavailable", "server_error", "429", "5xx"],
        "action": "demote_and_next",
        "target": "platform",
        "demoteSteps": 1
      },
      {
        "enabled": true,
        "categories": ["network", "timeout", "stream_error", "rate_limit", "provider_5xx", "provider_overloaded", "auth_error"],
        "codes": ["auth_failed", "invalid_api_key", "missing_credentials"],
        "statusCodes": [401, 403, 408, 429, 500, 502, 503, 504],
        "keywords": ["timeout", "network", "rate_limit", "overloaded", "temporarily_unavailable", "server_error", "auth_failed", "invalid_api_key", "missing_credentials", "unauthorized", "forbidden", "429", "5xx"],
        "action": "next"
      },
      {
        "enabled": true,
        "categories": ["request_error", "unsupported_model", "user_permission", "insufficient_balance"],
        "codes": ["bad_request", "invalid_request", "invalid_parameter", "missing_required", "unsupported_kind", "unsupported_model", "insufficient_balance", "permission_denied"],
        "keywords": ["invalid_parameter", "missing required", "bad request", "insufficient balance"],
        "action": "stop"
      }
    ]'::jsonb AS legacy_action_rules,
    '[
      {
        "enabled": true,
        "categories": ["network", "timeout", "stream_error", "provider_5xx", "provider_overloaded"],
        "action": "next"
      },
      {
        "enabled": true,
        "categories": ["rate_limit"],
        "codes": ["rate_limit"],
        "statusCodes": [429],
        "keywords": ["rate_limit", "429", "too many requests"],
        "action": "cooldown_and_next",
        "target": "model",
        "cooldownSeconds": 300
      },
      {
        "enabled": true,
        "keywords": ["rate limit", "too many requests", "temporarily_unavailable"],
        "action": "demote_and_next",
        "target": "platform",
        "demoteSteps": 1
      },
      {
        "enabled": true,
        "categories": ["auth_error"],
        "codes": ["auth_failed", "invalid_api_key", "missing_credentials"],
        "statusCodes": [401, 403],
        "keywords": ["invalid api key", "api key is invalid", "unauthorized", "forbidden", "permission denied"],
        "action": "disable_and_next",
        "target": "platform"
      },
      {
        "enabled": true,
        "categories": ["request_error", "unsupported_model", "user_permission", "insufficient_balance"],
        "codes": ["bad_request", "invalid_request", "invalid_parameter", "missing_required", "unsupported_kind", "unsupported_model", "insufficient_balance", "permission_denied"],
        "keywords": ["invalid_parameter", "missing required", "bad request", "insufficient balance"],
        "action": "stop"
      }
    ]'::jsonb AS compact_action_rules,
    '[
      {
        "enabled": true,
        "categories": ["rate_limit"],
        "codes": ["rate_limit", "too_many_requests", "too_many_request", "rate_limit_exceeded", "requests_rate_limited", "requests_limit_exceeded", "resource_exhausted", "throttled", "quota_exceeded", "quota_exhausted", "qps_limit", "rpm_limit", "tpm_limit"],
        "statusCodes": [429],
        "keywords": ["rate limit", "rate_limit", "rate-limit", "too many requests", "too_many_requests", "429", "throttle", "throttled", "resource exhausted", "quota exceeded", "quota_exceeded", "rate exceeded", "request limit", "requests per minute", "tokens per minute", "qps", "rpm", "tpm"],
        "action": "cooldown_and_next",
        "target": "model",
        "cooldownSeconds": 300
      },
      {
        "enabled": true,
        "categories": ["auth_error"],
        "codes": ["auth_failed", "invalid_api_key", "missing_credentials", "missing_credential", "unauthorized", "forbidden", "account_disabled", "billing_not_active", "permission_denied"],
        "statusCodes": [401, 403],
        "keywords": ["invalid api key", "api key is invalid", "invalid_api_key", "apikey invalid", "api key invalid", "unauthorized", "forbidden", "authentication", "auth failed", "credential", "missing credential", "permission denied", "access denied", "invalid signature", "signature mismatch", "secret key", "access key", "account disabled", "account_deactivated", "billing not active", "billing_not_active", "insufficient balance", "quota exceeded", "余额不足", "欠费"],
        "action": "disable_and_next",
        "target": "platform"
      },
      {
        "enabled": true,
        "categories": ["timeout", "provider_5xx", "provider_overloaded"],
        "codes": ["timeout", "server_error", "overloaded", "provider_failed", "invalid_response", "script_timeout", "request_asset_fetch_failed", "upload_failed", "upload_read_failed", "upload_source_read_failed"],
        "statusCodes": [408, 500, 502, 503, 504, 520, 521, 522, 523, 524, 529, 530],
        "keywords": ["timeout", "timed out", "deadline exceeded", "context deadline exceeded", "overloaded", "overload", "temporarily_unavailable", "temporarily unavailable", "temporary unavailable", "service unavailable", "server error", "internal server error", "bad gateway", "gateway timeout", "upstream timeout", "upstream error", "provider failed", "model is overloaded", "capacity", "try again later", "please try again later", "5xx", "500", "502", "503", "504"],
        "action": "demote_and_next",
        "target": "platform",
        "demoteSteps": 1
      },
      {
        "enabled": true,
        "categories": ["network", "stream_error"],
        "codes": ["network", "stream_read_error", "cancelled", "request_asset_fetch_failed", "upload_network", "upload_config_failed", "local_static_store_failed", "upload_source_fetch_failed"],
        "statusCodes": [],
        "keywords": ["network", "socket hang up", "socket closed", "connection reset", "connection refused", "connection aborted", "broken pipe", "econnreset", "econnrefused", "eai_again", "enotfound", "no such host", "dns", "tls handshake timeout", "unexpected eof", "connection closed", "stream error", "stream_read_error", "read failed", "fetch failed"],
        "action": "next"
      },
      {
        "enabled": true,
        "categories": ["request_error", "unsupported_model", "user_permission", "insufficient_balance"],
        "codes": ["bad_request", "invalid_request", "invalid_parameter", "missing_required", "unsupported_kind", "unsupported_model", "unsupported_operation", "unsupported_request_resolution", "request_asset_input_format_unsupported", "request_asset_expired", "request_asset_decode_failed", "request_asset_public_url_required", "upload_no_channel", "upload_unsupported_channel", "upload_source_too_large", "upload_source_invalid_url", "upload_source_unsupported_url", "upload_decode_failed", "invalid_proxy", "invalid_json_body", "invalid_multipart_body", "invalid_multipart_file", "invalid_multipart_image", "invalid_multipart_audio", "missing_configuration", "script_error", "cloned_voice_not_found", "cloned_voice_expired", "cloned_voice_unavailable", "insufficient_balance", "permission_denied"],
        "statusCodes": [400, 404, 405, 406, 409, 410, 411, 413, 415, 422],
        "keywords": ["invalid_parameter", "invalid parameter", "missing required", "missing_required", "bad request", "unsupported", "not supported", "does not support", "is not supported", "insufficient balance", "permission denied", "no permission", "invalid json", "invalid multipart", "image is required", "audio must", "prompt is required", "model is required", "exceeds", "too large", "expired", "not found", "required"],
        "action": "stop"
      }
    ]'::jsonb AS enhanced_action_rules
)
UPDATE gateway_runner_policies
SET failover_policy = jsonb_set(
      COALESCE(failover_policy, '{}'::jsonb),
      '{actionRules}',
      CASE
        WHEN failover_policy->'actionRules' IS NULL THEN defaults.enhanced_action_rules
        WHEN failover_policy->'actionRules' = defaults.legacy_action_rules THEN defaults.enhanced_action_rules
        WHEN failover_policy->'actionRules' = defaults.compact_action_rules THEN defaults.enhanced_action_rules
        ELSE failover_policy->'actionRules'
      END,
      true
    ) || jsonb_build_object(
      'legacyFieldsDeprecated', true,
      'deprecatedFields', to_jsonb(ARRAY['allowCategories', 'denyCategories', 'allowCodes', 'denyCodes', 'allowKeywords', 'denyKeywords', 'allowStatusCodes', 'denyStatusCodes', 'actions']::text[]),
      'replacedBy', 'actionRules'
    ),
    hard_stop_policy = COALESCE(hard_stop_policy, '{}'::jsonb) || jsonb_build_object(
      'deprecated', true,
      'replacedBy', 'failoverPolicy.actionRules'
    ),
    priority_demote_policy = jsonb_set(
      COALESCE(priority_demote_policy, '{}'::jsonb) || jsonb_build_object(
        'deprecated', true,
        'replacedBy', 'failoverPolicy.actionRules'
      ),
      '{derivedFromActionRules}',
      'true'::jsonb,
      true
    ),
    metadata = COALESCE(metadata, '{}'::jsonb) || jsonb_build_object('failoverActionRules', '0056_runner_failover_action_rules_v2'),
    updated_at = now()
FROM defaults
WHERE policy_key = 'default-runner-v1';
