ALTER TABLE gateway_tasks
  ADD COLUMN IF NOT EXISTS billing_version text DEFAULT 'effective-pricing-v2',
  ADD COLUMN IF NOT EXISTS billing_status text DEFAULT 'not_started',
  ADD COLUMN IF NOT EXISTS billing_currency text DEFAULT 'resource',
  ADD COLUMN IF NOT EXISTS pricing_snapshot jsonb DEFAULT '{}'::jsonb,
  ADD COLUMN IF NOT EXISTS request_fingerprint text,
  ADD COLUMN IF NOT EXISTS idempotency_key_hash text,
  ADD COLUMN IF NOT EXISTS idempotency_request_hash text,
  ADD COLUMN IF NOT EXISTS reservation_amount numeric(38, 9) DEFAULT 0,
  ADD COLUMN IF NOT EXISTS execution_token uuid,
  ADD COLUMN IF NOT EXISTS execution_lease_expires_at timestamptz,
  ADD COLUMN IF NOT EXISTS billing_updated_at timestamptz,
  ADD COLUMN IF NOT EXISTS billing_settled_at timestamptz;

UPDATE gateway_tasks
SET billing_version = COALESCE(billing_version, 'effective-pricing-v2'),
    billing_status = COALESCE(billing_status, 'not_started'),
    billing_currency = COALESCE(billing_currency, 'resource'),
    pricing_snapshot = COALESCE(pricing_snapshot, '{}'::jsonb),
    reservation_amount = COALESCE(reservation_amount, 0);

ALTER TABLE gateway_tasks
  ADD CONSTRAINT gateway_tasks_billing_status_v2_check CHECK (
    billing_status IN (
      'not_started', 'pending', 'processing', 'settled', 'released',
      'retryable_failed', 'manual_review', 'not_required'
    )
  ) NOT VALID,
  ADD CONSTRAINT gateway_tasks_reservation_amount_v2_check CHECK (reservation_amount >= 0) NOT VALID;

CREATE UNIQUE INDEX IF NOT EXISTS uniq_gateway_tasks_idempotency_hash_v2
  ON gateway_tasks(user_id, idempotency_key_hash)
  WHERE idempotency_key_hash IS NOT NULL;

CREATE INDEX IF NOT EXISTS idx_gateway_tasks_execution_lease_v2
  ON gateway_tasks(status, execution_lease_expires_at)
  WHERE status = 'running';

CREATE INDEX IF NOT EXISTS idx_gateway_tasks_billing_status_v2
  ON gateway_tasks(billing_status, billing_updated_at, created_at);

ALTER TABLE gateway_task_attempts
  ADD COLUMN IF NOT EXISTS pricing_snapshot jsonb DEFAULT '{}'::jsonb,
  ADD COLUMN IF NOT EXISTS request_fingerprint text,
  ADD COLUMN IF NOT EXISTS upstream_submission_status text DEFAULT 'not_submitted',
  ADD COLUMN IF NOT EXISTS upstream_submission_updated_at timestamptz;

UPDATE gateway_task_attempts
SET pricing_snapshot = COALESCE(pricing_snapshot, '{}'::jsonb),
    upstream_submission_status = COALESCE(upstream_submission_status, 'not_submitted');

ALTER TABLE gateway_task_attempts
  ADD CONSTRAINT gateway_task_attempts_submission_v2_check CHECK (
    upstream_submission_status IN ('not_submitted', 'submitting', 'response_received')
  ) NOT VALID;

ALTER TABLE settlement_outbox
  ADD COLUMN IF NOT EXISTS action text DEFAULT 'settle',
  ADD COLUMN IF NOT EXISTS amount numeric(38, 9) DEFAULT 0,
  ADD COLUMN IF NOT EXISTS currency text DEFAULT 'resource',
  ADD COLUMN IF NOT EXISTS pricing_snapshot jsonb DEFAULT '{}'::jsonb,
  ADD COLUMN IF NOT EXISTS locked_by text,
  ADD COLUMN IF NOT EXISTS lock_token uuid,
  ADD COLUMN IF NOT EXISTS locked_at timestamptz,
  ADD COLUMN IF NOT EXISTS last_error_code text,
  ADD COLUMN IF NOT EXISTS last_error_message text,
  ADD COLUMN IF NOT EXISTS completed_at timestamptz,
  ADD COLUMN IF NOT EXISTS manual_review_reason text,
  ADD COLUMN IF NOT EXISTS retry_idempotency_key_hash text,
  ADD COLUMN IF NOT EXISTS retry_requested_at timestamptz;

UPDATE settlement_outbox
SET action = COALESCE(action, 'settle'),
    amount = COALESCE(amount, 0),
    currency = COALESCE(currency, 'resource'),
    pricing_snapshot = COALESCE(pricing_snapshot, '{}'::jsonb);

ALTER TABLE settlement_outbox
  ADD CONSTRAINT settlement_outbox_action_v2_check CHECK (action IN ('settle', 'release')) NOT VALID,
  ADD CONSTRAINT settlement_outbox_status_v2_check CHECK (
    status IN ('pending', 'processing', 'retryable_failed', 'completed', 'manual_review')
  ) NOT VALID,
  ADD CONSTRAINT settlement_outbox_amount_v2_check CHECK (amount >= 0) NOT VALID;

CREATE INDEX IF NOT EXISTS idx_settlement_outbox_claim_v2
  ON settlement_outbox(status, next_attempt_at, created_at)
  WHERE status IN ('pending', 'retryable_failed');

CREATE INDEX IF NOT EXISTS idx_settlement_outbox_stale_v2
  ON settlement_outbox(status, locked_at)
  WHERE status = 'processing';

ALTER TABLE model_pricing_rules
  ADD COLUMN IF NOT EXISTS is_free boolean DEFAULT false;

UPDATE model_pricing_rules SET is_free = false WHERE is_free IS NULL;

UPDATE model_pricing_rules
SET calculator_type = CASE
      WHEN resource_type IN ('text_input', 'text_cached_input', 'text_output', 'text_total') THEN 'token_usage'
      WHEN resource_type = 'video' THEN 'duration_weight'
      ELSE calculator_type
    END,
    unit = CASE
      WHEN resource_type = 'video' AND unit = 'video' THEN '5s'
      ELSE unit
    END,
    updated_at = now()
WHERE (resource_type IN ('text_input', 'text_cached_input', 'text_output', 'text_total') AND calculator_type = 'unit_weight')
   OR (resource_type = 'video' AND (calculator_type = 'unit_weight' OR unit = 'video'));

ALTER TABLE model_pricing_rules
  ADD CONSTRAINT model_pricing_rules_explicit_free_v2_check CHECK (
    base_price > 0 OR (base_price = 0 AND is_free)
  ) NOT VALID;

ALTER TABLE gateway_wallet_accounts
  ADD CONSTRAINT gateway_wallet_nonnegative_frozen_v2_check CHECK (frozen_balance >= 0) NOT VALID,
  ADD CONSTRAINT gateway_wallet_balance_covers_frozen_v2_check CHECK (balance >= frozen_balance) NOT VALID;

CREATE TABLE IF NOT EXISTS gateway_wallet_account_audit_guards (
  account_id uuid PRIMARY KEY REFERENCES gateway_wallet_accounts(id) ON DELETE RESTRICT,
  created_at timestamptz NOT NULL DEFAULT now()
);

INSERT INTO gateway_wallet_account_audit_guards (account_id)
SELECT id FROM gateway_wallet_accounts
ON CONFLICT (account_id) DO NOTHING;

ALTER TABLE gateway_wallet_transactions
  ADD CONSTRAINT gateway_wallet_transactions_audit_guard_v2
  FOREIGN KEY (account_id) REFERENCES gateway_wallet_account_audit_guards(account_id) ON DELETE RESTRICT NOT VALID;

UPDATE gateway_tasks task
SET billing_status = 'not_required',
    billing_updated_at = now()
WHERE task.run_mode <> 'production'
   OR task.gateway_user_id IS NULL;

UPDATE gateway_tasks task
SET billing_status = 'settled',
    billing_currency = COALESCE(NULLIF(task.billing_summary->>'currency', ''), 'resource'),
    billing_updated_at = now(),
    billing_settled_at = COALESCE(task.finished_at, task.updated_at)
WHERE task.status = 'succeeded'
  AND EXISTS (
    SELECT 1
    FROM gateway_wallet_transactions transaction
    WHERE transaction.reference_type = 'gateway_task'
      AND transaction.reference_id = task.id::text
      AND transaction.transaction_type = 'task_billing'
  );

UPDATE gateway_tasks task
SET billing_status = 'manual_review',
    billing_currency = COALESCE(NULLIF(task.billing_summary->>'currency', ''), 'resource'),
    billing_updated_at = now()
WHERE task.status = 'succeeded'
  AND task.run_mode = 'production'
  AND task.gateway_user_id IS NOT NULL
  AND NOT EXISTS (
    SELECT 1
    FROM gateway_wallet_transactions transaction
    WHERE transaction.reference_type = 'gateway_task'
      AND transaction.reference_id = task.id::text
      AND transaction.transaction_type = 'task_billing'
  );

INSERT INTO settlement_outbox (
  task_id, event_type, action, amount, currency, pricing_snapshot, payload,
  status, next_attempt_at, manual_review_reason
)
SELECT task.id,
       'task.billing.review',
       'settle',
       COALESCE(task.final_charge_amount, 0),
       COALESCE(NULLIF(task.billing_summary->>'currency', ''), 'resource'),
       COALESCE(task.pricing_snapshot, '{}'::jsonb),
       jsonb_build_object(
         'taskId', task.id,
         'classification', 'historical_success_without_charge'
       ),
       'manual_review',
       now(),
       'historical_success_without_charge'
FROM gateway_tasks task
WHERE task.billing_status = 'manual_review'
  AND task.status = 'succeeded'
  AND task.run_mode = 'production'
  AND task.gateway_user_id IS NOT NULL
ON CONFLICT (task_id, event_type) DO NOTHING;

INSERT INTO settlement_outbox (
  task_id, event_type, action, amount, currency, payload, status, next_attempt_at
)
SELECT task.id,
       'task.billing.release',
       'release',
       transaction.amount,
       account.currency,
       jsonb_build_object(
         'taskId', task.id,
         'reservationIdempotencyKey', transaction.idempotency_key,
         'classification', 'historical_terminal_reservation'
       ),
       'pending',
       now()
FROM gateway_tasks task
JOIN gateway_wallet_transactions transaction
  ON transaction.reference_type = 'gateway_task'
 AND transaction.reference_id = task.id::text
 AND transaction.transaction_type = 'reserve'
JOIN gateway_wallet_accounts account ON account.id = transaction.account_id
WHERE task.status IN ('failed', 'cancelled')
  AND COALESCE(transaction.idempotency_key, '') <> ''
  AND NOT EXISTS (
    SELECT 1
    FROM gateway_wallet_transactions release
    WHERE release.account_id = transaction.account_id
      AND release.transaction_type = 'release'
      AND release.idempotency_key = transaction.idempotency_key || ':release'
  )
ON CONFLICT (task_id, event_type) DO NOTHING;

UPDATE gateway_tasks task
SET billing_status = 'pending',
    billing_updated_at = now()
WHERE EXISTS (
  SELECT 1
  FROM settlement_outbox outbox
  WHERE outbox.task_id = task.id
    AND outbox.action = 'release'
    AND outbox.status IN ('pending', 'processing', 'retryable_failed')
);
