CREATE EXTENSION IF NOT EXISTS pgcrypto;

CREATE TABLE IF NOT EXISTS model_catalog_providers (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  provider_key text NOT NULL UNIQUE,
  provider_code text NOT NULL,
  display_name text NOT NULL,
  provider_type text NOT NULL DEFAULT 'openai',
  icon_path text,
  source text NOT NULL DEFAULT 'gateway',
  capability_schema jsonb NOT NULL DEFAULT '{}'::jsonb,
  default_rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  status text NOT NULL DEFAULT 'active',
  metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS base_model_catalog (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  provider_id uuid REFERENCES model_catalog_providers(id) ON DELETE SET NULL,
  provider_key text NOT NULL,
  canonical_model_key text NOT NULL UNIQUE,
  provider_model_name text NOT NULL,
  model_type text NOT NULL,
  display_name text NOT NULL,
  capabilities jsonb NOT NULL DEFAULT '{}'::jsonb,
  base_billing_config jsonb NOT NULL DEFAULT '{}'::jsonb,
  default_rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  pricing_version integer NOT NULL DEFAULT 1,
  status text NOT NULL DEFAULT 'active',
  metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS integration_platforms (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  provider text NOT NULL,
  platform_key text NOT NULL UNIQUE DEFAULT ('platform_' || replace(gen_random_uuid()::text, '-', '')),
  name text NOT NULL,
  base_url text,
  auth_type text NOT NULL DEFAULT 'bearer',
  credentials jsonb NOT NULL DEFAULT '{}'::jsonb,
  config jsonb NOT NULL DEFAULT '{}'::jsonb,
  visibility_scope text NOT NULL DEFAULT 'global',
  tenant_id text,
  tenant_key text,
  default_pricing_mode text NOT NULL DEFAULT 'inherit_discount',
  default_discount_factor numeric NOT NULL DEFAULT 1,
  pricing_rule_set_id uuid,
  retry_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  priority integer NOT NULL DEFAULT 100,
  dynamic_priority integer,
  status text NOT NULL DEFAULT 'enabled',
  disabled_reason text,
  cooldown_until timestamptz,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  deleted_at timestamptz
);

ALTER TABLE IF EXISTS integration_platforms
  ADD COLUMN IF NOT EXISTS platform_key text,
  ADD COLUMN IF NOT EXISTS visibility_scope text NOT NULL DEFAULT 'global',
  ADD COLUMN IF NOT EXISTS tenant_id text,
  ADD COLUMN IF NOT EXISTS tenant_key text,
  ADD COLUMN IF NOT EXISTS default_pricing_mode text NOT NULL DEFAULT 'inherit_discount',
  ADD COLUMN IF NOT EXISTS default_discount_factor numeric NOT NULL DEFAULT 1,
  ADD COLUMN IF NOT EXISTS pricing_rule_set_id uuid,
  ADD COLUMN IF NOT EXISTS retry_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  ADD COLUMN IF NOT EXISTS rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  ADD COLUMN IF NOT EXISTS dynamic_priority integer,
  ADD COLUMN IF NOT EXISTS disabled_reason text,
  ADD COLUMN IF NOT EXISTS cooldown_until timestamptz,
  ADD COLUMN IF NOT EXISTS deleted_at timestamptz;

UPDATE integration_platforms
SET platform_key = 'platform_' || replace(id::text, '-', '')
WHERE platform_key IS NULL OR platform_key = '';

ALTER TABLE IF EXISTS integration_platforms
  ALTER COLUMN platform_key SET DEFAULT ('platform_' || replace(gen_random_uuid()::text, '-', '')),
  ALTER COLUMN platform_key SET NOT NULL;

CREATE TABLE IF NOT EXISTS model_pricing_rule_sets (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  rule_set_key text NOT NULL UNIQUE,
  name text NOT NULL,
  description text,
  category text NOT NULL DEFAULT 'general',
  currency text NOT NULL DEFAULT 'resource',
  status text NOT NULL DEFAULT 'active',
  metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS model_pricing_rules (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  rule_set_id uuid REFERENCES model_pricing_rule_sets(id) ON DELETE CASCADE,
  rule_key text NOT NULL DEFAULT ('rule_' || replace(gen_random_uuid()::text, '-', '')),
  display_name text NOT NULL DEFAULT '',
  scope_type text NOT NULL,
  scope_id uuid,
  resource_type text NOT NULL,
  unit text NOT NULL,
  base_price numeric NOT NULL,
  currency text NOT NULL DEFAULT 'resource',
  base_weight jsonb NOT NULL DEFAULT '{}'::jsonb,
  dynamic_weight jsonb NOT NULL DEFAULT '{}'::jsonb,
  calculator_type text NOT NULL DEFAULT 'unit_weight',
  dimension_schema jsonb NOT NULL DEFAULT '{}'::jsonb,
  formula_config jsonb NOT NULL DEFAULT '{}'::jsonb,
  priority integer NOT NULL DEFAULT 100,
  status text NOT NULL DEFAULT 'active',
  metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  effective_from timestamptz,
  effective_to timestamptz,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

ALTER TABLE IF EXISTS model_pricing_rules
  ADD COLUMN IF NOT EXISTS rule_set_id uuid REFERENCES model_pricing_rule_sets(id) ON DELETE CASCADE,
  ADD COLUMN IF NOT EXISTS rule_key text NOT NULL DEFAULT ('rule_' || replace(gen_random_uuid()::text, '-', '')),
  ADD COLUMN IF NOT EXISTS display_name text NOT NULL DEFAULT '',
  ADD COLUMN IF NOT EXISTS calculator_type text NOT NULL DEFAULT 'unit_weight',
  ADD COLUMN IF NOT EXISTS dimension_schema jsonb NOT NULL DEFAULT '{}'::jsonb,
  ADD COLUMN IF NOT EXISTS formula_config jsonb NOT NULL DEFAULT '{}'::jsonb,
  ADD COLUMN IF NOT EXISTS priority integer NOT NULL DEFAULT 100,
  ADD COLUMN IF NOT EXISTS status text NOT NULL DEFAULT 'active',
  ADD COLUMN IF NOT EXISTS metadata jsonb NOT NULL DEFAULT '{}'::jsonb;

CREATE TABLE IF NOT EXISTS gateway_user_groups (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  group_key text NOT NULL UNIQUE,
  name text NOT NULL,
  description text,
  source text NOT NULL DEFAULT 'gateway',
  priority integer NOT NULL DEFAULT 100,
  recharge_discount_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  billing_discount_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  quota_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  status text NOT NULL DEFAULT 'active',
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS gateway_tenants (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_key text NOT NULL UNIQUE,
  source text NOT NULL DEFAULT 'gateway',
  external_tenant_id text,
  name text NOT NULL,
  description text,
  default_user_group_id uuid REFERENCES gateway_user_groups(id) ON DELETE SET NULL,
  plan_key text,
  billing_profile jsonb NOT NULL DEFAULT '{}'::jsonb,
  rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  auth_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  status text NOT NULL DEFAULT 'active',
  synced_at timestamptz,
  source_updated_at timestamptz,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  deleted_at timestamptz,
  UNIQUE(source, external_tenant_id)
);

CREATE TABLE IF NOT EXISTS gateway_users (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  user_key text NOT NULL UNIQUE,
  source text NOT NULL DEFAULT 'gateway',
  external_user_id text,
  username text NOT NULL,
  display_name text,
  email text,
  phone text,
  avatar_url text,
  password_hash text,
  gateway_tenant_id uuid REFERENCES gateway_tenants(id) ON DELETE SET NULL,
  tenant_id text,
  tenant_key text,
  default_user_group_id uuid REFERENCES gateway_user_groups(id) ON DELETE SET NULL,
  roles jsonb NOT NULL DEFAULT '[]'::jsonb,
  auth_profile jsonb NOT NULL DEFAULT '{}'::jsonb,
  metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  status text NOT NULL DEFAULT 'active',
  last_login_at timestamptz,
  synced_at timestamptz,
  source_updated_at timestamptz,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  deleted_at timestamptz,
  UNIQUE(source, external_user_id)
);

CREATE TABLE IF NOT EXISTS gateway_user_group_memberships (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  group_id uuid NOT NULL REFERENCES gateway_user_groups(id) ON DELETE CASCADE,
  principal_type text NOT NULL,
  principal_id text NOT NULL,
  source text NOT NULL DEFAULT 'gateway',
  priority integer NOT NULL DEFAULT 100,
  effective_from timestamptz,
  effective_to timestamptz,
  status text NOT NULL DEFAULT 'active',
  metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  UNIQUE(group_id, principal_type, principal_id)
);

DO $$
BEGIN
  IF to_regclass('public.gateway_tenant_invitations') IS NOT NULL
     AND to_regclass('public.gateway_invitations') IS NULL THEN
    ALTER TABLE gateway_tenant_invitations RENAME TO gateway_invitations;
  END IF;
END $$;

DROP INDEX IF EXISTS idx_gateway_invitations_tenant;

CREATE TABLE IF NOT EXISTS gateway_invitations (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  invite_code text NOT NULL UNIQUE,
  max_uses integer,
  used_count integer NOT NULL DEFAULT 0,
  expires_at timestamptz,
  status text NOT NULL DEFAULT 'active',
  metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  created_by uuid REFERENCES gateway_users(id) ON DELETE SET NULL,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

ALTER TABLE IF EXISTS gateway_invitations
  DROP COLUMN IF EXISTS tenant_id,
  DROP COLUMN IF EXISTS role,
  DROP COLUMN IF EXISTS user_group_id;

CREATE TABLE IF NOT EXISTS gateway_api_keys (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  gateway_tenant_id uuid REFERENCES gateway_tenants(id) ON DELETE SET NULL,
  gateway_user_id uuid REFERENCES gateway_users(id) ON DELETE CASCADE,
  tenant_id text,
  tenant_key text,
  user_id text,
  key_prefix text NOT NULL,
  key_secret text,
  key_hash text NOT NULL UNIQUE,
  name text NOT NULL,
  scopes jsonb NOT NULL DEFAULT '[]'::jsonb,
  user_group_id uuid REFERENCES gateway_user_groups(id) ON DELETE SET NULL,
  rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  quota_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  status text NOT NULL DEFAULT 'active',
  expires_at timestamptz,
  last_used_at timestamptz,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  deleted_at timestamptz
);

CREATE TABLE IF NOT EXISTS gateway_wallet_accounts (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  gateway_tenant_id uuid REFERENCES gateway_tenants(id) ON DELETE SET NULL,
  gateway_user_id uuid REFERENCES gateway_users(id) ON DELETE CASCADE,
  tenant_id text,
  tenant_key text,
  user_id text,
  currency text NOT NULL DEFAULT 'resource',
  balance numeric NOT NULL DEFAULT 0,
  frozen_balance numeric NOT NULL DEFAULT 0,
  metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  status text NOT NULL DEFAULT 'active',
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  UNIQUE(gateway_user_id, currency)
);

CREATE TABLE IF NOT EXISTS gateway_wallet_transactions (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  wallet_account_id uuid REFERENCES gateway_wallet_accounts(id) ON DELETE SET NULL,
  gateway_tenant_id uuid REFERENCES gateway_tenants(id) ON DELETE SET NULL,
  gateway_user_id uuid REFERENCES gateway_users(id) ON DELETE SET NULL,
  transaction_type text NOT NULL,
  amount numeric NOT NULL,
  balance_after numeric NOT NULL,
  reference_type text,
  reference_id text,
  metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS gateway_recharge_orders (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  gateway_tenant_id uuid REFERENCES gateway_tenants(id) ON DELETE SET NULL,
  gateway_user_id uuid REFERENCES gateway_users(id) ON DELETE SET NULL,
  tenant_id text,
  tenant_key text,
  user_id text,
  order_no text NOT NULL UNIQUE,
  amount numeric NOT NULL,
  bonus_amount numeric NOT NULL DEFAULT 0,
  currency text NOT NULL DEFAULT 'resource',
  payment_provider text,
  payment_payload jsonb NOT NULL DEFAULT '{}'::jsonb,
  status text NOT NULL DEFAULT 'pending',
  paid_at timestamptz,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS platform_models (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  platform_id uuid REFERENCES integration_platforms(id) ON DELETE CASCADE,
  base_model_id uuid REFERENCES base_model_catalog(id) ON DELETE SET NULL,
  model_name text NOT NULL,
  model_alias text,
  model_type jsonb NOT NULL DEFAULT '[]'::jsonb,
  display_name text NOT NULL DEFAULT '',
  capability_override jsonb NOT NULL DEFAULT '{}'::jsonb,
  capabilities jsonb NOT NULL DEFAULT '{}'::jsonb,
  pricing_mode text NOT NULL DEFAULT 'inherit_discount',
  discount_factor numeric,
  pricing_rule_set_id uuid REFERENCES model_pricing_rule_sets(id) ON DELETE SET NULL,
  billing_config_override jsonb NOT NULL DEFAULT '{}'::jsonb,
  billing_config jsonb NOT NULL DEFAULT '{}'::jsonb,
  permission_config jsonb NOT NULL DEFAULT '{}'::jsonb,
  retry_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  enabled boolean NOT NULL DEFAULT true,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  UNIQUE(platform_id, model_name)
);

ALTER TABLE IF EXISTS platform_models
  ADD COLUMN IF NOT EXISTS base_model_id uuid REFERENCES base_model_catalog(id) ON DELETE SET NULL,
  ADD COLUMN IF NOT EXISTS model_alias text,
  ADD COLUMN IF NOT EXISTS capability_override jsonb NOT NULL DEFAULT '{}'::jsonb,
  ADD COLUMN IF NOT EXISTS pricing_mode text NOT NULL DEFAULT 'inherit_discount',
  ADD COLUMN IF NOT EXISTS discount_factor numeric,
  ADD COLUMN IF NOT EXISTS pricing_rule_set_id uuid REFERENCES model_pricing_rule_sets(id) ON DELETE SET NULL,
  ADD COLUMN IF NOT EXISTS billing_config_override jsonb NOT NULL DEFAULT '{}'::jsonb,
  ADD COLUMN IF NOT EXISTS permission_config jsonb NOT NULL DEFAULT '{}'::jsonb,
  ADD COLUMN IF NOT EXISTS retry_policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  ADD COLUMN IF NOT EXISTS rate_limit_policy jsonb NOT NULL DEFAULT '{}'::jsonb;

CREATE TABLE IF NOT EXISTS gateway_tasks (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  external_task_id text,
  kind text NOT NULL,
  run_mode text NOT NULL DEFAULT 'production',
  user_id text NOT NULL,
  gateway_user_id uuid REFERENCES gateway_users(id) ON DELETE SET NULL,
  user_source text NOT NULL DEFAULT 'gateway',
  gateway_tenant_id uuid REFERENCES gateway_tenants(id) ON DELETE SET NULL,
  tenant_id text,
  tenant_key text,
  api_key_id text,
  user_group_id uuid REFERENCES gateway_user_groups(id) ON DELETE SET NULL,
  user_group_key text,
  user_group_policy_snapshot jsonb NOT NULL DEFAULT '{}'::jsonb,
  model text NOT NULL,
  model_type text,
  request jsonb NOT NULL DEFAULT '{}'::jsonb,
  normalized_request jsonb NOT NULL DEFAULT '{}'::jsonb,
  status text NOT NULL DEFAULT 'queued',
  queue_key text NOT NULL DEFAULT 'default',
  priority integer NOT NULL DEFAULT 100,
  idempotency_key text,
  remote_task_id text,
  remote_task_payload jsonb,
  simulation_profile jsonb,
  simulation_seed text,
  locked_by text,
  locked_at timestamptz,
  heartbeat_at timestamptz,
  next_run_at timestamptz NOT NULL DEFAULT now(),
  attempt_count integer NOT NULL DEFAULT 0,
  max_attempts integer NOT NULL DEFAULT 1,
  result jsonb,
  billings jsonb,
  error text,
  error_code text,
  error_message text,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  finished_at timestamptz
);

ALTER TABLE IF EXISTS gateway_tasks
  ADD COLUMN IF NOT EXISTS external_task_id text,
  ADD COLUMN IF NOT EXISTS run_mode text NOT NULL DEFAULT 'production',
  ADD COLUMN IF NOT EXISTS gateway_user_id uuid REFERENCES gateway_users(id) ON DELETE SET NULL,
  ADD COLUMN IF NOT EXISTS user_source text NOT NULL DEFAULT 'gateway',
  ADD COLUMN IF NOT EXISTS gateway_tenant_id uuid REFERENCES gateway_tenants(id) ON DELETE SET NULL,
  ADD COLUMN IF NOT EXISTS tenant_key text,
  ADD COLUMN IF NOT EXISTS api_key_id text,
  ADD COLUMN IF NOT EXISTS user_group_id uuid REFERENCES gateway_user_groups(id) ON DELETE SET NULL,
  ADD COLUMN IF NOT EXISTS user_group_key text,
  ADD COLUMN IF NOT EXISTS user_group_policy_snapshot jsonb NOT NULL DEFAULT '{}'::jsonb,
  ADD COLUMN IF NOT EXISTS model_type text,
  ADD COLUMN IF NOT EXISTS normalized_request jsonb NOT NULL DEFAULT '{}'::jsonb,
  ADD COLUMN IF NOT EXISTS queue_key text NOT NULL DEFAULT 'default',
  ADD COLUMN IF NOT EXISTS priority integer NOT NULL DEFAULT 100,
  ADD COLUMN IF NOT EXISTS idempotency_key text,
  ADD COLUMN IF NOT EXISTS remote_task_id text,
  ADD COLUMN IF NOT EXISTS remote_task_payload jsonb,
  ADD COLUMN IF NOT EXISTS simulation_profile jsonb,
  ADD COLUMN IF NOT EXISTS simulation_seed text,
  ADD COLUMN IF NOT EXISTS locked_by text,
  ADD COLUMN IF NOT EXISTS locked_at timestamptz,
  ADD COLUMN IF NOT EXISTS heartbeat_at timestamptz,
  ADD COLUMN IF NOT EXISTS next_run_at timestamptz NOT NULL DEFAULT now(),
  ADD COLUMN IF NOT EXISTS attempt_count integer NOT NULL DEFAULT 0,
  ADD COLUMN IF NOT EXISTS max_attempts integer NOT NULL DEFAULT 1,
  ADD COLUMN IF NOT EXISTS error_code text,
  ADD COLUMN IF NOT EXISTS error_message text,
  ADD COLUMN IF NOT EXISTS finished_at timestamptz;

CREATE TABLE IF NOT EXISTS gateway_task_attempts (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE,
  attempt_no integer NOT NULL,
  platform_id uuid REFERENCES integration_platforms(id) ON DELETE SET NULL,
  platform_model_id uuid REFERENCES platform_models(id) ON DELETE SET NULL,
  client_id text,
  queue_key text NOT NULL,
  status text NOT NULL,
  retryable boolean NOT NULL DEFAULT false,
  simulated boolean NOT NULL DEFAULT false,
  remote_task_id text,
  request_snapshot jsonb NOT NULL DEFAULT '{}'::jsonb,
  response_snapshot jsonb,
  error_code text,
  error_message text,
  started_at timestamptz NOT NULL DEFAULT now(),
  finished_at timestamptz,
  UNIQUE(task_id, attempt_no)
);

CREATE TABLE IF NOT EXISTS gateway_task_events (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE,
  seq bigint NOT NULL,
  event_type text NOT NULL,
  status text,
  phase text,
  progress numeric,
  message text,
  payload jsonb NOT NULL DEFAULT '{}'::jsonb,
  simulated boolean NOT NULL DEFAULT false,
  created_at timestamptz NOT NULL DEFAULT now(),
  UNIQUE(task_id, seq)
);

CREATE TABLE IF NOT EXISTS gateway_task_callback_outbox (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE,
  event_id uuid REFERENCES gateway_task_events(id) ON DELETE SET NULL,
  seq bigint NOT NULL,
  callback_url text NOT NULL,
  payload jsonb NOT NULL,
  status text NOT NULL DEFAULT 'pending',
  attempts integer NOT NULL DEFAULT 0,
  next_attempt_at timestamptz NOT NULL DEFAULT now(),
  last_error text,
  delivered_at timestamptz,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  UNIQUE(task_id, seq, callback_url)
);

CREATE TABLE IF NOT EXISTS runtime_client_states (
  client_id text PRIMARY KEY,
  platform_id uuid REFERENCES integration_platforms(id) ON DELETE SET NULL,
  provider text NOT NULL,
  method_name text NOT NULL,
  queue_key text NOT NULL,
  running_count integer NOT NULL DEFAULT 0,
  waiting_count integer NOT NULL DEFAULT 0,
  limiter_ratio numeric NOT NULL DEFAULT 0,
  cooldown_until timestamptz,
  last_assigned_at timestamptz,
  last_error text,
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS gateway_upload_assets (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  task_id uuid REFERENCES gateway_tasks(id) ON DELETE SET NULL,
  source text NOT NULL,
  server_main_file_id text,
  url text NOT NULL,
  object_key text,
  content_type text,
  size bigint,
  checksum text,
  metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS gateway_retry_policies (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  scope_type text NOT NULL,
  scope_key text NOT NULL,
  enabled boolean NOT NULL DEFAULT true,
  policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  UNIQUE(scope_type, scope_key)
);

CREATE TABLE IF NOT EXISTS gateway_rate_limit_policies (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  scope_type text NOT NULL,
  scope_key text NOT NULL,
  policy jsonb NOT NULL DEFAULT '{}'::jsonb,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  UNIQUE(scope_type, scope_key)
);

CREATE TABLE IF NOT EXISTS gateway_rate_limit_counters (
  scope_type text NOT NULL,
  scope_key text NOT NULL,
  metric text NOT NULL,
  window_start timestamptz NOT NULL,
  limit_value numeric NOT NULL,
  used_value numeric NOT NULL DEFAULT 0,
  reserved_value numeric NOT NULL DEFAULT 0,
  reset_at timestamptz NOT NULL,
  updated_at timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY(scope_type, scope_key, metric, window_start)
);

CREATE TABLE IF NOT EXISTS gateway_concurrency_leases (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE,
  attempt_id uuid REFERENCES gateway_task_attempts(id) ON DELETE SET NULL,
  scope_type text NOT NULL,
  scope_key text NOT NULL,
  lease_value numeric NOT NULL DEFAULT 1,
  acquired_at timestamptz NOT NULL DEFAULT now(),
  expires_at timestamptz NOT NULL,
  released_at timestamptz
);

ALTER TABLE IF EXISTS settlement_outbox
  ADD COLUMN IF NOT EXISTS event_type text NOT NULL DEFAULT 'task.settlement.requested',
  ADD COLUMN IF NOT EXISTS payload jsonb NOT NULL DEFAULT '{}'::jsonb,
  ADD COLUMN IF NOT EXISTS status text NOT NULL DEFAULT 'pending',
  ADD COLUMN IF NOT EXISTS attempts integer NOT NULL DEFAULT 0,
  ADD COLUMN IF NOT EXISTS next_attempt_at timestamptz NOT NULL DEFAULT now(),
  ADD COLUMN IF NOT EXISTS updated_at timestamptz NOT NULL DEFAULT now();

CREATE TABLE IF NOT EXISTS settlement_outbox (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  task_id uuid NOT NULL REFERENCES gateway_tasks(id) ON DELETE CASCADE,
  event_type text NOT NULL DEFAULT 'task.settlement.requested',
  payload jsonb NOT NULL,
  status text NOT NULL DEFAULT 'pending',
  attempts integer NOT NULL DEFAULT 0,
  next_attempt_at timestamptz NOT NULL DEFAULT now(),
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  UNIQUE(task_id, event_type)
);

CREATE UNIQUE INDEX IF NOT EXISTS uniq_integration_platforms_platform_key
  ON integration_platforms(platform_key);

CREATE INDEX IF NOT EXISTS idx_model_catalog_provider_status
  ON model_catalog_providers(status);
CREATE INDEX IF NOT EXISTS idx_base_model_catalog_provider
  ON base_model_catalog(provider_key, model_type, status);
CREATE INDEX IF NOT EXISTS idx_base_model_catalog_capabilities
  ON base_model_catalog USING gin(capabilities);
CREATE INDEX IF NOT EXISTS idx_integration_platforms_provider_status
  ON integration_platforms(provider, status);
CREATE INDEX IF NOT EXISTS idx_integration_platforms_status_priority
  ON integration_platforms(status, priority, dynamic_priority);
CREATE INDEX IF NOT EXISTS idx_integration_platforms_cooldown
  ON integration_platforms(cooldown_until);
CREATE INDEX IF NOT EXISTS idx_integration_platforms_tenant_scope
  ON integration_platforms(visibility_scope, tenant_id, tenant_key, status);
CREATE INDEX IF NOT EXISTS idx_model_pricing_scope
  ON model_pricing_rules(scope_type, scope_id, resource_type);
CREATE INDEX IF NOT EXISTS idx_model_pricing_rule_set
  ON model_pricing_rules(rule_set_id, resource_type, priority);
CREATE UNIQUE INDEX IF NOT EXISTS idx_model_pricing_rule_set_key
  ON model_pricing_rules(rule_set_id, rule_key)
  WHERE rule_set_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_model_pricing_effective
  ON model_pricing_rules(effective_from, effective_to);
CREATE INDEX IF NOT EXISTS idx_gateway_user_groups_status_priority
  ON gateway_user_groups(status, priority);
CREATE INDEX IF NOT EXISTS idx_gateway_tenants_source_external
  ON gateway_tenants(source, external_tenant_id)
  WHERE external_tenant_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_gateway_tenants_status
  ON gateway_tenants(status, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_gateway_users_source_external
  ON gateway_users(source, external_user_id)
  WHERE external_user_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_gateway_users_status
  ON gateway_users(status, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_gateway_users_tenant
  ON gateway_users(tenant_id, tenant_key, status);
CREATE INDEX IF NOT EXISTS idx_user_group_membership_principal
  ON gateway_user_group_memberships(principal_type, principal_id, status);
CREATE INDEX IF NOT EXISTS idx_user_group_membership_effective
  ON gateway_user_group_memberships(effective_from, effective_to);
CREATE INDEX IF NOT EXISTS idx_gateway_invitations_status
  ON gateway_invitations(status, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_gateway_invitations_expiry
  ON gateway_invitations(expires_at);
CREATE INDEX IF NOT EXISTS idx_gateway_api_keys_prefix
  ON gateway_api_keys(key_prefix, status);
CREATE INDEX IF NOT EXISTS idx_gateway_api_keys_user
  ON gateway_api_keys(gateway_user_id, status);
CREATE INDEX IF NOT EXISTS idx_gateway_wallet_accounts_tenant
  ON gateway_wallet_accounts(gateway_tenant_id, status);
CREATE INDEX IF NOT EXISTS idx_gateway_wallet_transactions_user
  ON gateway_wallet_transactions(gateway_user_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_gateway_recharge_orders_user
  ON gateway_recharge_orders(gateway_user_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_platform_models_base
  ON platform_models(base_model_id);
CREATE INDEX IF NOT EXISTS idx_platform_models_lookup
  ON platform_models(model_name, enabled);

CREATE INDEX IF NOT EXISTS idx_platform_models_model_type
  ON platform_models USING gin(model_type);
CREATE INDEX IF NOT EXISTS idx_platform_models_alias
  ON platform_models(model_alias);
CREATE INDEX IF NOT EXISTS idx_platform_models_capabilities
  ON platform_models USING gin(capabilities);
CREATE UNIQUE INDEX IF NOT EXISTS uniq_platform_models_model
  ON platform_models(platform_id, model_name, model_type);
CREATE INDEX IF NOT EXISTS idx_gateway_tasks_queue
  ON gateway_tasks(status, next_run_at, priority, created_at);
CREATE INDEX IF NOT EXISTS idx_gateway_tasks_lease
  ON gateway_tasks(status, heartbeat_at);
CREATE INDEX IF NOT EXISTS idx_gateway_tasks_user_created
  ON gateway_tasks(user_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_gateway_tasks_external
  ON gateway_tasks(external_task_id);
CREATE UNIQUE INDEX IF NOT EXISTS uniq_gateway_tasks_idempotency
  ON gateway_tasks(user_id, idempotency_key)
  WHERE idempotency_key IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_gateway_attempts_task
  ON gateway_task_attempts(task_id);
CREATE INDEX IF NOT EXISTS idx_gateway_attempts_client
  ON gateway_task_attempts(client_id, started_at DESC);
CREATE INDEX IF NOT EXISTS idx_gateway_events_task_created
  ON gateway_task_events(task_id, created_at);
CREATE INDEX IF NOT EXISTS idx_task_callback_outbox_pending
  ON gateway_task_callback_outbox(status, next_attempt_at);
CREATE INDEX IF NOT EXISTS idx_task_callback_outbox_task
  ON gateway_task_callback_outbox(task_id, seq);
CREATE INDEX IF NOT EXISTS idx_runtime_client_queue
  ON runtime_client_states(queue_key, cooldown_until);
CREATE INDEX IF NOT EXISTS idx_runtime_client_platform
  ON runtime_client_states(platform_id);
CREATE INDEX IF NOT EXISTS idx_gateway_upload_task
  ON gateway_upload_assets(task_id);
CREATE INDEX IF NOT EXISTS idx_gateway_upload_file
  ON gateway_upload_assets(server_main_file_id);
CREATE INDEX IF NOT EXISTS idx_concurrency_leases_active
  ON gateway_concurrency_leases(scope_type, scope_key, released_at, expires_at);
CREATE INDEX IF NOT EXISTS idx_concurrency_leases_task
  ON gateway_concurrency_leases(task_id);
CREATE UNIQUE INDEX IF NOT EXISTS uniq_settlement_outbox_task_event
  ON settlement_outbox(task_id, event_type);
