-- ============================================================================
-- SCHEMA: financial
-- TABLE: wallet_transactions
-- DESCRIPTION: Registro de todas las transacciones de wallet
-- VERSION: 1.0.0
-- CREATED: 2026-01-10
-- ============================================================================

-- Enum para tipo de transaccion
DO $$ BEGIN
    CREATE TYPE financial.transaction_type AS ENUM (
        'CREDIT_PURCHASE',      -- Compra de creditos con tarjeta
        'AGENT_FUNDING',        -- Fondeo a Money Manager
        'AGENT_WITHDRAWAL',     -- Retiro de Money Manager
        'AGENT_PROFIT',         -- Ganancia de Money Manager
        'AGENT_LOSS',           -- Perdida de Money Manager
        'PRODUCT_PURCHASE',     -- Compra de producto (one-time)
        'SUBSCRIPTION_CHARGE',  -- Cargo de suscripcion
        'PREDICTION_PURCHASE',  -- Compra de prediccion individual
        'REFUND',               -- Devolucion
        'PROMO_CREDIT',         -- Creditos promocionales agregados
        'PROMO_EXPIRY',         -- Expiracion de creditos promo
        'ADJUSTMENT',           -- Ajuste manual (admin)
        'TRANSFER_IN',          -- Transferencia recibida
        'TRANSFER_OUT',         -- Transferencia enviada
        'FEE'                   -- Comision de plataforma
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Enum para estado de transaccion
DO $$ BEGIN
    CREATE TYPE financial.transaction_status AS ENUM (
        'pending',      -- Pendiente de confirmacion
        'completed',    -- Completada exitosamente
        'failed',       -- Fallida
        'cancelled',    -- Cancelada
        'reversed'      -- Revertida
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Tabla de transacciones
CREATE TABLE IF NOT EXISTS financial.wallet_transactions (
    -- Identificadores
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    wallet_id UUID NOT NULL REFERENCES financial.wallets(id) ON DELETE RESTRICT,
    tenant_id UUID NOT NULL,

    -- Tipo y estado
    type financial.transaction_type NOT NULL,
    status financial.transaction_status NOT NULL DEFAULT 'completed',

    -- Montos
    amount DECIMAL(15, 4) NOT NULL,
    balance_before DECIMAL(15, 4) NOT NULL,
    balance_after DECIMAL(15, 4) NOT NULL,

    -- Descripcion
    description TEXT,

    -- Referencias a entidades relacionadas
    reference_type VARCHAR(50),
    reference_id UUID,

    -- Para compras de creditos (Stripe)
    stripe_payment_intent_id VARCHAR(255),
    stripe_charge_id VARCHAR(255),
    payment_method VARCHAR(50),

    -- Para transferencias entre wallets
    related_wallet_id UUID REFERENCES financial.wallets(id),
    related_transaction_id UUID,

    -- Metadata adicional
    metadata JSONB DEFAULT '{}',

    -- Auditoria
    ip_address INET,
    user_agent TEXT,
    created_by UUID,

    -- Timestamps
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    processed_at TIMESTAMPTZ,

    -- Indices parciales para busquedas frecuentes
    CONSTRAINT valid_amount CHECK (
        (type IN ('CREDIT_PURCHASE', 'AGENT_PROFIT', 'PROMO_CREDIT', 'REFUND', 'TRANSFER_IN') AND amount > 0)
        OR
        (type IN ('AGENT_FUNDING', 'PRODUCT_PURCHASE', 'SUBSCRIPTION_CHARGE', 'PREDICTION_PURCHASE', 'PROMO_EXPIRY', 'TRANSFER_OUT', 'FEE', 'AGENT_LOSS') AND amount < 0)
        OR
        (type IN ('ADJUSTMENT', 'AGENT_WITHDRAWAL'))
    )
);

COMMENT ON TABLE financial.wallet_transactions IS
'Registro inmutable de todas las transacciones de wallet. Cada operacion crea un registro.';

-- Indices
CREATE INDEX IF NOT EXISTS idx_wallet_tx_wallet_id
    ON financial.wallet_transactions(wallet_id);

CREATE INDEX IF NOT EXISTS idx_wallet_tx_tenant_id
    ON financial.wallet_transactions(tenant_id);

CREATE INDEX IF NOT EXISTS idx_wallet_tx_type
    ON financial.wallet_transactions(type);

CREATE INDEX IF NOT EXISTS idx_wallet_tx_status
    ON financial.wallet_transactions(status);

CREATE INDEX IF NOT EXISTS idx_wallet_tx_created_at
    ON financial.wallet_transactions(created_at DESC);

CREATE INDEX IF NOT EXISTS idx_wallet_tx_reference
    ON financial.wallet_transactions(reference_type, reference_id)
    WHERE reference_id IS NOT NULL;

CREATE INDEX IF NOT EXISTS idx_wallet_tx_stripe
    ON financial.wallet_transactions(stripe_payment_intent_id)
    WHERE stripe_payment_intent_id IS NOT NULL;

-- Funcion para crear transaccion y actualizar wallet atomicamente
CREATE OR REPLACE FUNCTION financial.create_wallet_transaction(
    p_wallet_id UUID,
    p_type financial.transaction_type,
    p_amount DECIMAL(15, 4),
    p_description TEXT DEFAULT NULL,
    p_reference_type VARCHAR(50) DEFAULT NULL,
    p_reference_id UUID DEFAULT NULL,
    p_metadata JSONB DEFAULT '{}'
)
RETURNS UUID AS $$
DECLARE
    v_wallet financial.wallets%ROWTYPE;
    v_transaction_id UUID;
    v_new_balance DECIMAL(15, 4);
BEGIN
    -- Lock wallet row
    SELECT * INTO v_wallet
    FROM financial.wallets
    WHERE id = p_wallet_id
    FOR UPDATE;

    IF NOT FOUND THEN
        RAISE EXCEPTION 'Wallet not found: %', p_wallet_id;
    END IF;

    IF v_wallet.status != 'active' THEN
        RAISE EXCEPTION 'Wallet is not active: %', v_wallet.status;
    END IF;

    -- Calcular nuevo balance
    v_new_balance := v_wallet.balance + p_amount;

    -- Validar que no quede negativo
    IF v_new_balance < 0 THEN
        RAISE EXCEPTION 'Insufficient balance. Current: %, Required: %',
            v_wallet.balance, ABS(p_amount);
    END IF;

    -- Validar limites si es debito
    IF p_amount < 0 THEN
        IF ABS(p_amount) > v_wallet.single_transaction_limit THEN
            RAISE EXCEPTION 'Exceeds single transaction limit: %',
                v_wallet.single_transaction_limit;
        END IF;

        IF v_wallet.daily_spent + ABS(p_amount) > v_wallet.daily_spend_limit THEN
            RAISE EXCEPTION 'Exceeds daily spend limit: %',
                v_wallet.daily_spend_limit;
        END IF;
    END IF;

    -- Crear transaccion
    INSERT INTO financial.wallet_transactions (
        wallet_id, tenant_id, type, amount,
        balance_before, balance_after,
        description, reference_type, reference_id, metadata
    ) VALUES (
        p_wallet_id, v_wallet.tenant_id, p_type, p_amount,
        v_wallet.balance, v_new_balance,
        p_description, p_reference_type, p_reference_id, p_metadata
    ) RETURNING id INTO v_transaction_id;

    -- Actualizar wallet
    UPDATE financial.wallets SET
        balance = v_new_balance,
        total_credited = CASE WHEN p_amount > 0 THEN total_credited + p_amount ELSE total_credited END,
        total_debited = CASE WHEN p_amount < 0 THEN total_debited + ABS(p_amount) ELSE total_debited END,
        daily_spent = CASE WHEN p_amount < 0 THEN daily_spent + ABS(p_amount) ELSE daily_spent END,
        monthly_spent = CASE WHEN p_amount < 0 THEN monthly_spent + ABS(p_amount) ELSE monthly_spent END,
        last_transaction_at = NOW()
    WHERE id = p_wallet_id;

    RETURN v_transaction_id;
END;
$$ LANGUAGE plpgsql;

-- RLS Policy
ALTER TABLE financial.wallet_transactions ENABLE ROW LEVEL SECURITY;

CREATE POLICY wallet_tx_tenant_isolation ON financial.wallet_transactions
    FOR ALL
    USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);

-- Grants
GRANT SELECT, INSERT ON financial.wallet_transactions TO trading_app;
GRANT SELECT ON financial.wallet_transactions TO trading_readonly;
