-- =====================================================
-- ORBIQUANT IA - FINANCIAL SCHEMA TRIGGERS
-- =====================================================
-- Description: Automated triggers for data integrity and audit
-- Schema: financial
-- =====================================================

-- =====================================================
-- TRIGGER: Update timestamps
-- =====================================================

CREATE OR REPLACE FUNCTION financial.update_timestamp()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$;

-- Apply to all tables with updated_at
CREATE TRIGGER trigger_wallets_updated_at
  BEFORE UPDATE ON financial.wallets
  FOR EACH ROW
  EXECUTE FUNCTION financial.update_timestamp();

CREATE TRIGGER trigger_transactions_updated_at
  BEFORE UPDATE ON financial.wallet_transactions
  FOR EACH ROW
  EXECUTE FUNCTION financial.update_timestamp();

CREATE TRIGGER trigger_subscriptions_updated_at
  BEFORE UPDATE ON financial.subscriptions
  FOR EACH ROW
  EXECUTE FUNCTION financial.update_timestamp();

CREATE TRIGGER trigger_payments_updated_at
  BEFORE UPDATE ON financial.payments
  FOR EACH ROW
  EXECUTE FUNCTION financial.update_timestamp();

CREATE TRIGGER trigger_invoices_updated_at
  BEFORE UPDATE ON financial.invoices
  FOR EACH ROW
  EXECUTE FUNCTION financial.update_timestamp();

CREATE TRIGGER trigger_exchange_rates_updated_at
  BEFORE UPDATE ON financial.currency_exchange_rates
  FOR EACH ROW
  EXECUTE FUNCTION financial.update_timestamp();

CREATE TRIGGER trigger_wallet_limits_updated_at
  BEFORE UPDATE ON financial.wallet_limits
  FOR EACH ROW
  EXECUTE FUNCTION financial.update_timestamp();

-- =====================================================
-- TRIGGER: Auto-generate invoice number
-- =====================================================

CREATE OR REPLACE FUNCTION financial.generate_invoice_number()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
  IF NEW.invoice_number IS NULL THEN
    NEW.invoice_number := 'INV-' || TO_CHAR(NOW(), 'YYYYMM') || '-' ||
                          LPAD(nextval('financial.invoice_number_seq')::TEXT, 6, '0');
  END IF;
  RETURN NEW;
END;
$$;

CREATE TRIGGER trigger_invoice_number
  BEFORE INSERT ON financial.invoices
  FOR EACH ROW
  EXECUTE FUNCTION financial.generate_invoice_number();

-- =====================================================
-- TRIGGER: Validate wallet balance consistency
-- =====================================================

CREATE OR REPLACE FUNCTION financial.validate_wallet_balance()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
  -- Validar que balance = available + pending
  IF NEW.balance != (NEW.available_balance + NEW.pending_balance) THEN
    RAISE EXCEPTION 'Balance consistency error: balance (%) != available (%) + pending (%)',
      NEW.balance, NEW.available_balance, NEW.pending_balance;
  END IF;

  -- Validar que no haya negativos
  IF NEW.balance < 0 OR NEW.available_balance < 0 OR NEW.pending_balance < 0 THEN
    RAISE EXCEPTION 'Negative balance detected: balance=%, available=%, pending=%',
      NEW.balance, NEW.available_balance, NEW.pending_balance;
  END IF;

  RETURN NEW;
END;
$$;

CREATE TRIGGER trigger_wallet_balance_validation
  BEFORE INSERT OR UPDATE ON financial.wallets
  FOR EACH ROW
  EXECUTE FUNCTION financial.validate_wallet_balance();

-- =====================================================
-- TRIGGER: Audit wallet status changes
-- =====================================================

CREATE OR REPLACE FUNCTION financial.audit_wallet_status_change()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
  -- Solo auditar si cambió el status
  IF TG_OP = 'UPDATE' AND OLD.status IS DISTINCT FROM NEW.status THEN
    INSERT INTO financial.wallet_audit_log (
      wallet_id,
      action,
      actor_type,
      old_values,
      new_values,
      reason
    ) VALUES (
      NEW.id,
      'status_changed',
      'system',
      jsonb_build_object('status', OLD.status),
      jsonb_build_object('status', NEW.status),
      'Status changed from ' || OLD.status::TEXT || ' to ' || NEW.status::TEXT
    );

    -- Si se cerró, registrar timestamp
    IF NEW.status = 'closed' AND NEW.closed_at IS NULL THEN
      NEW.closed_at := NOW();
    END IF;
  END IF;

  RETURN NEW;
END;
$$;

CREATE TRIGGER trigger_wallet_status_audit
  BEFORE UPDATE ON financial.wallets
  FOR EACH ROW
  EXECUTE FUNCTION financial.audit_wallet_status_change();

-- =====================================================
-- TRIGGER: Prevent modification of completed transactions
-- =====================================================

CREATE OR REPLACE FUNCTION financial.protect_completed_transactions()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
  IF OLD.status = 'completed' AND NEW.status != 'completed' THEN
    RAISE EXCEPTION 'Cannot modify completed transaction %', OLD.id;
  END IF;

  IF OLD.status = 'completed' AND (
    OLD.amount != NEW.amount OR
    OLD.wallet_id != NEW.wallet_id OR
    OLD.transaction_type != NEW.transaction_type
  ) THEN
    RAISE EXCEPTION 'Cannot modify core fields of completed transaction %', OLD.id;
  END IF;

  RETURN NEW;
END;
$$;

CREATE TRIGGER trigger_protect_completed_tx
  BEFORE UPDATE ON financial.wallet_transactions
  FOR EACH ROW
  EXECUTE FUNCTION financial.protect_completed_transactions();

-- =====================================================
-- TRIGGER: Set payment succeeded_at timestamp
-- =====================================================

CREATE OR REPLACE FUNCTION financial.set_payment_timestamps()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
  -- Set succeeded_at when status changes to succeeded
  IF NEW.status = 'succeeded' AND OLD.status != 'succeeded' THEN
    NEW.succeeded_at := NOW();
  END IF;

  -- Set failed_at when status changes to failed
  IF NEW.status = 'failed' AND OLD.status != 'failed' THEN
    NEW.failed_at := NOW();
  END IF;

  RETURN NEW;
END;
$$;

CREATE TRIGGER trigger_payment_timestamps
  BEFORE UPDATE ON financial.payments
  FOR EACH ROW
  EXECUTE FUNCTION financial.set_payment_timestamps();

-- =====================================================
-- TRIGGER: Update subscription ended_at
-- =====================================================

CREATE OR REPLACE FUNCTION financial.set_subscription_ended_at()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
  -- Set ended_at when status changes to cancelled and cancel_at_period_end is false
  IF NEW.status = 'cancelled' AND
     OLD.status != 'cancelled' AND
     NOT NEW.cancel_at_period_end AND
     NEW.ended_at IS NULL THEN
    NEW.ended_at := NOW();
  END IF;

  RETURN NEW;
END;
$$;

CREATE TRIGGER trigger_subscription_ended_at
  BEFORE UPDATE ON financial.subscriptions
  FOR EACH ROW
  EXECUTE FUNCTION financial.set_subscription_ended_at();

-- =====================================================
-- TRIGGER: Validate transaction currency matches wallet
-- =====================================================

CREATE OR REPLACE FUNCTION financial.validate_transaction_currency()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
DECLARE
  v_wallet_currency financial.currency_code;
BEGIN
  -- Get wallet currency
  SELECT currency INTO v_wallet_currency
  FROM financial.wallets
  WHERE id = NEW.wallet_id;

  IF NOT FOUND THEN
    RAISE EXCEPTION 'Wallet % not found', NEW.wallet_id;
  END IF;

  -- Validate currency match
  IF NEW.currency != v_wallet_currency THEN
    RAISE EXCEPTION 'Transaction currency (%) does not match wallet currency (%)',
      NEW.currency, v_wallet_currency;
  END IF;

  RETURN NEW;
END;
$$;

CREATE TRIGGER trigger_transaction_currency_validation
  BEFORE INSERT ON financial.wallet_transactions
  FOR EACH ROW
  EXECUTE FUNCTION financial.validate_transaction_currency();

COMMENT ON FUNCTION financial.update_timestamp IS 'Auto-update updated_at timestamp';
COMMENT ON FUNCTION financial.generate_invoice_number IS 'Auto-generate invoice number with format INV-YYYYMM-XXXXXX';
COMMENT ON FUNCTION financial.validate_wallet_balance IS 'Ensure balance = available + pending';
COMMENT ON FUNCTION financial.audit_wallet_status_change IS 'Log wallet status changes to audit log';
COMMENT ON FUNCTION financial.protect_completed_transactions IS 'Prevent modification of completed transactions';
COMMENT ON FUNCTION financial.set_payment_timestamps IS 'Auto-set succeeded_at and failed_at timestamps';
COMMENT ON FUNCTION financial.set_subscription_ended_at IS 'Auto-set ended_at when subscription is cancelled';
COMMENT ON FUNCTION financial.validate_transaction_currency IS 'Ensure transaction currency matches wallet currency';
