-- ============================================================================
-- SCHEMA: investment
-- TABLES: agent_allocations, allocation_transactions, profit_distributions
-- DESCRIPTION: Sistema de fondeo de Money Manager Agents desde Wallet
-- VERSION: 1.0.0
-- CREATED: 2026-01-10
-- DEPENDS: financial.wallets, agents (existing)
-- ============================================================================

-- Crear schema si no existe
CREATE SCHEMA IF NOT EXISTS investment;

-- Enum para tipo de agente
DO $$ BEGIN
    CREATE TYPE investment.agent_type AS ENUM (
        'ATLAS',    -- Conservador - bajo riesgo
        'ORION',    -- Moderado - riesgo medio
        'NOVA'      -- Agresivo - alto riesgo
    );
EXCEPTION WHEN duplicate_object THEN null; END $$;

-- Enum para estado de allocation
DO $$ BEGIN
    CREATE TYPE investment.allocation_status AS ENUM (
        'pending',      -- Esperando confirmacion
        'active',       -- Activa y operando
        'paused',       -- Pausada por usuario
        'liquidating',  -- En proceso de liquidacion
        'closed'        -- Cerrada
    );
EXCEPTION WHEN duplicate_object THEN null; END $$;

-- Enum para tipo de transaccion de allocation
DO $$ BEGIN
    CREATE TYPE investment.allocation_tx_type AS ENUM (
        'INITIAL_FUNDING',      -- Fondeo inicial
        'ADDITIONAL_FUNDING',   -- Fondeo adicional
        'PARTIAL_WITHDRAWAL',   -- Retiro parcial
        'FULL_WITHDRAWAL',      -- Retiro total
        'PROFIT_REALIZED',      -- Ganancia realizada
        'LOSS_REALIZED',        -- Perdida realizada
        'FEE_CHARGED',          -- Comision cobrada
        'REBALANCE'             -- Rebalanceo
    );
EXCEPTION WHEN duplicate_object THEN null; END $$;

-- Tabla principal de allocations (fondeo a agentes)
CREATE TABLE IF NOT EXISTS investment.agent_allocations (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,
    user_id UUID NOT NULL,
    wallet_id UUID NOT NULL,

    -- Agente
    agent_type investment.agent_type NOT NULL,
    agent_instance_id UUID, -- Referencia a instancia especifica del agente

    -- Montos
    initial_amount DECIMAL(15, 4) NOT NULL CHECK (initial_amount > 0),
    current_amount DECIMAL(15, 4) NOT NULL CHECK (current_amount >= 0),
    total_deposited DECIMAL(15, 4) NOT NULL DEFAULT 0,
    total_withdrawn DECIMAL(15, 4) NOT NULL DEFAULT 0,

    -- Performance
    total_profit DECIMAL(15, 4) NOT NULL DEFAULT 0,
    total_loss DECIMAL(15, 4) NOT NULL DEFAULT 0,
    realized_pnl DECIMAL(15, 4) GENERATED ALWAYS AS (total_profit - total_loss) STORED,
    unrealized_pnl DECIMAL(15, 4) DEFAULT 0,

    -- Metricas
    roi_percentage DECIMAL(10, 4) DEFAULT 0,
    max_drawdown DECIMAL(10, 4) DEFAULT 0,
    win_rate DECIMAL(5, 2) DEFAULT 0,
    total_trades INT DEFAULT 0,
    winning_trades INT DEFAULT 0,
    losing_trades INT DEFAULT 0,

    -- Configuracion
    risk_level VARCHAR(20), -- LOW, MEDIUM, HIGH
    auto_compound BOOLEAN DEFAULT FALSE,
    compound_threshold DECIMAL(15, 4) DEFAULT 100,

    -- Limites
    max_allocation DECIMAL(15, 4),
    stop_loss_percentage DECIMAL(5, 2) DEFAULT 20,
    take_profit_percentage DECIMAL(5, 2),

    -- Estado
    status investment.allocation_status NOT NULL DEFAULT 'pending',

    -- Fechas importantes
    activated_at TIMESTAMPTZ,
    last_trade_at TIMESTAMPTZ,
    paused_at TIMESTAMPTZ,
    closed_at TIMESTAMPTZ,

    -- Metadata
    metadata JSONB DEFAULT '{}',
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW(),

    -- Un usuario puede tener multiples allocations al mismo agente
    -- pero con limites
    CONSTRAINT positive_performance CHECK (
        total_deposited >= total_withdrawn
        OR status IN ('closed', 'liquidating')
    )
);

COMMENT ON TABLE investment.agent_allocations IS
'Fondeos de usuarios a Money Manager Agents (Atlas, Orion, Nova)';

-- Tabla de transacciones de allocation
CREATE TABLE IF NOT EXISTS investment.allocation_transactions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    allocation_id UUID NOT NULL REFERENCES investment.agent_allocations(id),
    tenant_id UUID NOT NULL,

    -- Tipo
    type investment.allocation_tx_type NOT NULL,

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

    -- Referencia a wallet transaction
    wallet_transaction_id UUID,

    -- Para trades
    trade_id UUID,
    trade_symbol VARCHAR(20),
    trade_pnl DECIMAL(15, 4),

    -- Descripcion
    description TEXT,

    -- Metadata
    metadata JSONB DEFAULT '{}',
    created_at TIMESTAMPTZ DEFAULT NOW()
);

COMMENT ON TABLE investment.allocation_transactions IS
'Historial de todas las transacciones en allocations de agentes';

-- Tabla de distribuciones de profit
CREATE TABLE IF NOT EXISTS investment.profit_distributions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    allocation_id UUID NOT NULL REFERENCES investment.agent_allocations(id),
    tenant_id UUID NOT NULL,
    user_id UUID NOT NULL,

    -- Periodo
    period_start TIMESTAMPTZ NOT NULL,
    period_end TIMESTAMPTZ NOT NULL,

    -- Montos
    gross_profit DECIMAL(15, 4) NOT NULL,
    platform_fee DECIMAL(15, 4) NOT NULL DEFAULT 0,
    agent_fee DECIMAL(15, 4) NOT NULL DEFAULT 0,
    net_profit DECIMAL(15, 4) NOT NULL,

    -- Fee percentages aplicados
    platform_fee_rate DECIMAL(5, 4) DEFAULT 0.10,  -- 10%
    agent_fee_rate DECIMAL(5, 4) DEFAULT 0.20,     -- 20%

    -- Distribucion
    distributed_to_wallet BOOLEAN DEFAULT FALSE,
    wallet_transaction_id UUID,
    distributed_at TIMESTAMPTZ,

    -- Compounding
    compounded_amount DECIMAL(15, 4) DEFAULT 0,

    created_at TIMESTAMPTZ DEFAULT NOW()
);

COMMENT ON TABLE investment.profit_distributions IS
'Distribuciones periodicas de ganancias de agentes a usuarios';

-- Tabla de configuracion de agentes por tenant
CREATE TABLE IF NOT EXISTS investment.agent_configs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    agent_type investment.agent_type NOT NULL,

    -- Limites
    min_allocation DECIMAL(15, 4) DEFAULT 100,
    max_allocation_per_user DECIMAL(15, 4) DEFAULT 10000,
    max_total_allocation DECIMAL(15, 4) DEFAULT 1000000,

    -- Fees
    platform_fee_rate DECIMAL(5, 4) DEFAULT 0.10,
    performance_fee_rate DECIMAL(5, 4) DEFAULT 0.20,

    -- Estado
    is_active BOOLEAN DEFAULT TRUE,
    accepting_new_allocations BOOLEAN DEFAULT TRUE,

    -- Descripcion
    description TEXT,
    risk_disclosure TEXT,

    -- Stats agregados
    total_users INT DEFAULT 0,
    total_allocated DECIMAL(15, 4) DEFAULT 0,
    total_profit_generated DECIMAL(15, 4) DEFAULT 0,

    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW(),

    UNIQUE(tenant_id, agent_type)
);

COMMENT ON TABLE investment.agent_configs IS
'Configuracion de Money Manager Agents por tenant';

-- Indices para agent_allocations
CREATE INDEX IF NOT EXISTS idx_alloc_tenant ON investment.agent_allocations(tenant_id);
CREATE INDEX IF NOT EXISTS idx_alloc_user ON investment.agent_allocations(user_id);
CREATE INDEX IF NOT EXISTS idx_alloc_wallet ON investment.agent_allocations(wallet_id);
CREATE INDEX IF NOT EXISTS idx_alloc_agent ON investment.agent_allocations(agent_type);
CREATE INDEX IF NOT EXISTS idx_alloc_status ON investment.agent_allocations(status);
CREATE INDEX IF NOT EXISTS idx_alloc_active ON investment.agent_allocations(status, agent_type)
    WHERE status = 'active';

-- Indices para allocation_transactions
CREATE INDEX IF NOT EXISTS idx_alloc_tx_alloc ON investment.allocation_transactions(allocation_id);
CREATE INDEX IF NOT EXISTS idx_alloc_tx_tenant ON investment.allocation_transactions(tenant_id);
CREATE INDEX IF NOT EXISTS idx_alloc_tx_type ON investment.allocation_transactions(type);
CREATE INDEX IF NOT EXISTS idx_alloc_tx_created ON investment.allocation_transactions(created_at DESC);

-- Indices para profit_distributions
CREATE INDEX IF NOT EXISTS idx_profit_dist_alloc ON investment.profit_distributions(allocation_id);
CREATE INDEX IF NOT EXISTS idx_profit_dist_user ON investment.profit_distributions(user_id);
CREATE INDEX IF NOT EXISTS idx_profit_dist_period ON investment.profit_distributions(period_start, period_end);
CREATE INDEX IF NOT EXISTS idx_profit_dist_pending ON investment.profit_distributions(distributed_to_wallet)
    WHERE distributed_to_wallet = FALSE;

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

DROP TRIGGER IF EXISTS alloc_updated ON investment.agent_allocations;
CREATE TRIGGER alloc_updated
    BEFORE UPDATE ON investment.agent_allocations
    FOR EACH ROW
    EXECUTE FUNCTION investment.update_timestamp();

DROP TRIGGER IF EXISTS agent_config_updated ON investment.agent_configs;
CREATE TRIGGER agent_config_updated
    BEFORE UPDATE ON investment.agent_configs
    FOR EACH ROW
    EXECUTE FUNCTION investment.update_timestamp();

-- Funcion para crear allocation (fondear agente)
CREATE OR REPLACE FUNCTION investment.create_allocation(
    p_tenant_id UUID,
    p_user_id UUID,
    p_wallet_id UUID,
    p_agent_type investment.agent_type,
    p_amount DECIMAL(15, 4),
    p_auto_compound BOOLEAN DEFAULT FALSE
)
RETURNS UUID AS $$
DECLARE
    v_config investment.agent_configs%ROWTYPE;
    v_allocation_id UUID;
    v_wallet_tx_id UUID;
BEGIN
    -- Get agent config
    SELECT * INTO v_config
    FROM investment.agent_configs
    WHERE tenant_id = p_tenant_id
      AND agent_type = p_agent_type;

    IF NOT FOUND OR NOT v_config.is_active THEN
        RAISE EXCEPTION 'Agent % is not available', p_agent_type;
    END IF;

    IF NOT v_config.accepting_new_allocations THEN
        RAISE EXCEPTION 'Agent % is not accepting new allocations', p_agent_type;
    END IF;

    IF p_amount < v_config.min_allocation THEN
        RAISE EXCEPTION 'Minimum allocation is %', v_config.min_allocation;
    END IF;

    IF p_amount > v_config.max_allocation_per_user THEN
        RAISE EXCEPTION 'Maximum allocation per user is %', v_config.max_allocation_per_user;
    END IF;

    -- Debit from wallet (this validates balance)
    v_wallet_tx_id := financial.create_wallet_transaction(
        p_wallet_id,
        'AGENT_FUNDING',
        -p_amount,
        'Funding to ' || p_agent_type || ' agent',
        'agent_allocation',
        NULL,
        jsonb_build_object('agent_type', p_agent_type)
    );

    -- Create allocation
    INSERT INTO investment.agent_allocations (
        tenant_id, user_id, wallet_id, agent_type,
        initial_amount, current_amount, total_deposited,
        auto_compound, risk_level, status
    ) VALUES (
        p_tenant_id, p_user_id, p_wallet_id, p_agent_type,
        p_amount, p_amount, p_amount,
        p_auto_compound,
        CASE p_agent_type
            WHEN 'ATLAS' THEN 'LOW'
            WHEN 'ORION' THEN 'MEDIUM'
            WHEN 'NOVA' THEN 'HIGH'
        END,
        'active'
    ) RETURNING id INTO v_allocation_id;

    -- Update allocation with wallet tx reference
    UPDATE investment.agent_allocations
    SET activated_at = NOW(),
        metadata = jsonb_build_object('initial_wallet_tx', v_wallet_tx_id)
    WHERE id = v_allocation_id;

    -- Record allocation transaction
    INSERT INTO investment.allocation_transactions (
        allocation_id, tenant_id, type,
        amount, balance_before, balance_after,
        wallet_transaction_id, description
    ) VALUES (
        v_allocation_id, p_tenant_id, 'INITIAL_FUNDING',
        p_amount, 0, p_amount,
        v_wallet_tx_id, 'Initial funding'
    );

    -- Update agent config stats
    UPDATE investment.agent_configs
    SET total_users = total_users + 1,
        total_allocated = total_allocated + p_amount
    WHERE tenant_id = p_tenant_id AND agent_type = p_agent_type;

    RETURN v_allocation_id;
END;
$$ LANGUAGE plpgsql;

-- Funcion para retirar de allocation
CREATE OR REPLACE FUNCTION investment.withdraw_from_allocation(
    p_allocation_id UUID,
    p_amount DECIMAL(15, 4),
    p_full_withdrawal BOOLEAN DEFAULT FALSE
)
RETURNS UUID AS $$
DECLARE
    v_alloc investment.agent_allocations%ROWTYPE;
    v_wallet_tx_id UUID;
    v_withdraw_amount DECIMAL(15, 4);
    v_tx_type investment.allocation_tx_type;
BEGIN
    -- Lock allocation
    SELECT * INTO v_alloc
    FROM investment.agent_allocations
    WHERE id = p_allocation_id
    FOR UPDATE;

    IF NOT FOUND THEN
        RAISE EXCEPTION 'Allocation not found';
    END IF;

    IF v_alloc.status NOT IN ('active', 'paused') THEN
        RAISE EXCEPTION 'Cannot withdraw from allocation in status %', v_alloc.status;
    END IF;

    -- Determine withdrawal amount
    IF p_full_withdrawal THEN
        v_withdraw_amount := v_alloc.current_amount;
        v_tx_type := 'FULL_WITHDRAWAL';
    ELSE
        IF p_amount > v_alloc.current_amount THEN
            RAISE EXCEPTION 'Insufficient balance. Available: %', v_alloc.current_amount;
        END IF;
        v_withdraw_amount := p_amount;
        v_tx_type := 'PARTIAL_WITHDRAWAL';
    END IF;

    -- Credit to wallet
    v_wallet_tx_id := financial.create_wallet_transaction(
        v_alloc.wallet_id,
        'AGENT_WITHDRAWAL',
        v_withdraw_amount,
        'Withdrawal from ' || v_alloc.agent_type || ' agent',
        'agent_allocation',
        p_allocation_id,
        jsonb_build_object('agent_type', v_alloc.agent_type)
    );

    -- Record transaction
    INSERT INTO investment.allocation_transactions (
        allocation_id, tenant_id, type,
        amount, balance_before, balance_after,
        wallet_transaction_id, description
    ) VALUES (
        p_allocation_id, v_alloc.tenant_id, v_tx_type,
        -v_withdraw_amount, v_alloc.current_amount,
        v_alloc.current_amount - v_withdraw_amount,
        v_wallet_tx_id,
        CASE WHEN p_full_withdrawal THEN 'Full withdrawal' ELSE 'Partial withdrawal' END
    );

    -- Update allocation
    UPDATE investment.agent_allocations
    SET current_amount = current_amount - v_withdraw_amount,
        total_withdrawn = total_withdrawn + v_withdraw_amount,
        status = CASE WHEN p_full_withdrawal THEN 'closed' ELSE status END,
        closed_at = CASE WHEN p_full_withdrawal THEN NOW() ELSE closed_at END
    WHERE id = p_allocation_id;

    -- Update agent config stats
    UPDATE investment.agent_configs
    SET total_allocated = total_allocated - v_withdraw_amount,
        total_users = CASE WHEN p_full_withdrawal THEN total_users - 1 ELSE total_users END
    WHERE tenant_id = v_alloc.tenant_id AND agent_type = v_alloc.agent_type;

    RETURN v_wallet_tx_id;
END;
$$ LANGUAGE plpgsql;

-- Funcion para registrar profit/loss de trade
CREATE OR REPLACE FUNCTION investment.record_trade_result(
    p_allocation_id UUID,
    p_trade_id UUID,
    p_symbol VARCHAR(20),
    p_pnl DECIMAL(15, 4),
    p_is_win BOOLEAN
)
RETURNS VOID AS $$
DECLARE
    v_alloc investment.agent_allocations%ROWTYPE;
    v_tx_type investment.allocation_tx_type;
BEGIN
    SELECT * INTO v_alloc
    FROM investment.agent_allocations
    WHERE id = p_allocation_id
    FOR UPDATE;

    v_tx_type := CASE WHEN p_pnl >= 0 THEN 'PROFIT_REALIZED' ELSE 'LOSS_REALIZED' END;

    -- Record transaction
    INSERT INTO investment.allocation_transactions (
        allocation_id, tenant_id, type,
        amount, balance_before, balance_after,
        trade_id, trade_symbol, trade_pnl
    ) VALUES (
        p_allocation_id, v_alloc.tenant_id, v_tx_type,
        p_pnl, v_alloc.current_amount, v_alloc.current_amount + p_pnl,
        p_trade_id, p_symbol, p_pnl
    );

    -- Update allocation
    UPDATE investment.agent_allocations
    SET current_amount = current_amount + p_pnl,
        total_profit = CASE WHEN p_pnl > 0 THEN total_profit + p_pnl ELSE total_profit END,
        total_loss = CASE WHEN p_pnl < 0 THEN total_loss + ABS(p_pnl) ELSE total_loss END,
        total_trades = total_trades + 1,
        winning_trades = CASE WHEN p_is_win THEN winning_trades + 1 ELSE winning_trades END,
        losing_trades = CASE WHEN NOT p_is_win THEN losing_trades + 1 ELSE losing_trades END,
        win_rate = CASE
            WHEN total_trades > 0 THEN
                (winning_trades + CASE WHEN p_is_win THEN 1 ELSE 0 END)::DECIMAL * 100 /
                (total_trades + 1)
            ELSE 0
        END,
        roi_percentage = CASE
            WHEN total_deposited > 0 THEN
                ((current_amount + p_pnl - total_deposited + total_withdrawn) / total_deposited) * 100
            ELSE 0
        END,
        last_trade_at = NOW()
    WHERE id = p_allocation_id;
END;
$$ LANGUAGE plpgsql;

-- RLS
ALTER TABLE investment.agent_allocations ENABLE ROW LEVEL SECURITY;
ALTER TABLE investment.allocation_transactions ENABLE ROW LEVEL SECURITY;
ALTER TABLE investment.profit_distributions ENABLE ROW LEVEL SECURITY;
ALTER TABLE investment.agent_configs ENABLE ROW LEVEL SECURITY;

CREATE POLICY alloc_tenant ON investment.agent_allocations
    FOR ALL
    USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);

CREATE POLICY alloc_tx_tenant ON investment.allocation_transactions
    FOR ALL
    USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);

CREATE POLICY profit_tenant ON investment.profit_distributions
    FOR ALL
    USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);

CREATE POLICY config_tenant ON investment.agent_configs
    FOR ALL
    USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);

-- Grants
GRANT SELECT, INSERT, UPDATE ON investment.agent_allocations TO trading_app;
GRANT SELECT, INSERT ON investment.allocation_transactions TO trading_app;
GRANT SELECT, INSERT, UPDATE ON investment.profit_distributions TO trading_app;
GRANT SELECT, UPDATE ON investment.agent_configs TO trading_app;
GRANT SELECT ON investment.agent_allocations TO trading_readonly;
GRANT SELECT ON investment.allocation_transactions TO trading_readonly;
GRANT SELECT ON investment.profit_distributions TO trading_readonly;
GRANT SELECT ON investment.agent_configs TO trading_readonly;
