-- ============================================================================
-- SCHEMA: trading
-- TABLES: paper_trading_accounts, paper_trading_positions
-- DESCRIPTION: Sistema de paper trading (cuentas demo)
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 3 - DDL Implementation Roadmap Q1-2026
-- ============================================================================

-- ============================================================================
-- TABLA: paper_trading_accounts
-- ============================================================================

CREATE TABLE IF NOT EXISTS trading.paper_trading_accounts (
    -- Identificadores
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users.users(id) ON DELETE CASCADE,

    -- Informacion de cuenta
    name VARCHAR(100) NOT NULL DEFAULT 'Paper Account',
    description TEXT,

    -- Capital
    initial_balance DECIMAL(15, 2) NOT NULL DEFAULT 10000,
    current_balance DECIMAL(15, 2) NOT NULL DEFAULT 10000,
    currency VARCHAR(3) NOT NULL DEFAULT 'USD',

    -- Configuracion
    leverage INTEGER NOT NULL DEFAULT 100,
    margin_call_level DECIMAL(5, 2) DEFAULT 50,     -- % de margen para margin call
    stop_out_level DECIMAL(5, 2) DEFAULT 20,        -- % de margen para stop out

    -- Estado
    is_active BOOLEAN NOT NULL DEFAULT TRUE,
    is_default BOOLEAN NOT NULL DEFAULT FALSE,

    -- Margen
    used_margin DECIMAL(15, 2) NOT NULL DEFAULT 0,
    free_margin DECIMAL(15, 2) NOT NULL DEFAULT 10000,
    margin_level DECIMAL(10, 2),                    -- (Equity / Used Margin) * 100

    -- Equity y P&L
    equity DECIMAL(15, 2) NOT NULL DEFAULT 10000,   -- Balance + Unrealized P&L
    unrealized_pnl DECIMAL(15, 4) NOT NULL DEFAULT 0,
    realized_pnl DECIMAL(15, 4) NOT NULL DEFAULT 0,

    -- Estadisticas
    total_trades INTEGER NOT NULL DEFAULT 0,
    winning_trades INTEGER NOT NULL DEFAULT 0,
    losing_trades INTEGER NOT NULL DEFAULT 0,
    win_rate DECIMAL(5, 2) DEFAULT 0,
    profit_factor DECIMAL(10, 4) DEFAULT 0,
    max_drawdown DECIMAL(15, 4) DEFAULT 0,
    max_drawdown_percent DECIMAL(5, 2) DEFAULT 0,

    -- Rendimiento
    total_profit DECIMAL(15, 4) NOT NULL DEFAULT 0,
    total_loss DECIMAL(15, 4) NOT NULL DEFAULT 0,
    net_profit DECIMAL(15, 4) NOT NULL DEFAULT 0,
    return_percent DECIMAL(10, 4) DEFAULT 0,

    -- Posiciones actuales
    open_positions_count INTEGER NOT NULL DEFAULT 0,

    -- Resets
    reset_count INTEGER NOT NULL DEFAULT 0,
    last_reset_at TIMESTAMPTZ,

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

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

    -- Constraints
    CONSTRAINT paper_accounts_balance_check CHECK (initial_balance > 0)
);

COMMENT ON TABLE trading.paper_trading_accounts IS
'Cuentas de paper trading (demo) para practicar sin dinero real';

-- Indices para paper_trading_accounts
CREATE INDEX IF NOT EXISTS idx_paper_accounts_tenant
    ON trading.paper_trading_accounts(tenant_id);

CREATE INDEX IF NOT EXISTS idx_paper_accounts_user
    ON trading.paper_trading_accounts(user_id);

CREATE INDEX IF NOT EXISTS idx_paper_accounts_active
    ON trading.paper_trading_accounts(user_id, is_active)
    WHERE is_active = TRUE;

CREATE INDEX IF NOT EXISTS idx_paper_accounts_default
    ON trading.paper_trading_accounts(user_id, is_default)
    WHERE is_default = TRUE;

-- ============================================================================
-- TABLA: paper_trading_positions
-- ============================================================================

CREATE TABLE IF NOT EXISTS trading.paper_trading_positions (
    -- Identificadores
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    account_id UUID NOT NULL REFERENCES trading.paper_trading_accounts(id) ON DELETE CASCADE,
    tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users.users(id) ON DELETE CASCADE,

    -- Simbolo
    symbol VARCHAR(20) NOT NULL,
    symbol_id UUID REFERENCES trading.symbols(id),

    -- Direccion y estado
    direction trading.trade_direction NOT NULL,
    status trading.position_status NOT NULL DEFAULT 'open',
    order_type trading.order_type NOT NULL DEFAULT 'market',

    -- Tamaño
    lot_size DECIMAL(10, 4) NOT NULL,

    -- Precios
    entry_price DECIMAL(15, 8) NOT NULL,
    current_price DECIMAL(15, 8),
    exit_price DECIMAL(15, 8),

    -- Stop Loss / Take Profit
    stop_loss DECIMAL(15, 8),
    take_profit DECIMAL(15, 8),
    trailing_stop_enabled BOOLEAN NOT NULL DEFAULT FALSE,
    trailing_stop_distance DECIMAL(10, 4),

    -- P&L
    profit_loss DECIMAL(15, 4),
    profit_loss_pips DECIMAL(10, 4),
    unrealized_pnl DECIMAL(15, 4) NOT NULL DEFAULT 0,

    -- Margen
    margin_used DECIMAL(15, 4),

    -- Comisiones simuladas
    commission DECIMAL(15, 4) DEFAULT 0,
    swap DECIMAL(15, 4) DEFAULT 0,

    -- Analisis
    max_favorable_excursion DECIMAL(15, 4),
    max_adverse_excursion DECIMAL(15, 4),

    -- Notas
    notes TEXT,
    tags VARCHAR(50)[],

    -- Timestamps
    opened_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    closed_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

COMMENT ON TABLE trading.paper_trading_positions IS
'Posiciones de paper trading asociadas a cuentas demo';

-- Indices para paper_trading_positions
CREATE INDEX IF NOT EXISTS idx_paper_positions_account
    ON trading.paper_trading_positions(account_id);

CREATE INDEX IF NOT EXISTS idx_paper_positions_user
    ON trading.paper_trading_positions(user_id);

CREATE INDEX IF NOT EXISTS idx_paper_positions_tenant
    ON trading.paper_trading_positions(tenant_id);

CREATE INDEX IF NOT EXISTS idx_paper_positions_symbol
    ON trading.paper_trading_positions(symbol);

CREATE INDEX IF NOT EXISTS idx_paper_positions_status
    ON trading.paper_trading_positions(status);

CREATE INDEX IF NOT EXISTS idx_paper_positions_open
    ON trading.paper_trading_positions(account_id, status)
    WHERE status = 'open';

-- ============================================================================
-- TRIGGERS
-- ============================================================================

-- Trigger para updated_at en accounts
DROP TRIGGER IF EXISTS paper_account_updated_at ON trading.paper_trading_accounts;
CREATE TRIGGER paper_account_updated_at
    BEFORE UPDATE ON trading.paper_trading_accounts
    FOR EACH ROW
    EXECUTE FUNCTION trading.update_trading_timestamp();

-- Trigger para updated_at en positions
DROP TRIGGER IF EXISTS paper_position_updated_at ON trading.paper_trading_positions;
CREATE TRIGGER paper_position_updated_at
    BEFORE UPDATE ON trading.paper_trading_positions
    FOR EACH ROW
    EXECUTE FUNCTION trading.update_trading_timestamp();

-- Trigger para asegurar solo una cuenta default
CREATE OR REPLACE FUNCTION trading.ensure_single_default_paper_account()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.is_default = TRUE THEN
        UPDATE trading.paper_trading_accounts
        SET is_default = FALSE
        WHERE user_id = NEW.user_id
          AND id != NEW.id
          AND is_default = TRUE;
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS paper_account_single_default ON trading.paper_trading_accounts;
CREATE TRIGGER paper_account_single_default
    BEFORE INSERT OR UPDATE OF is_default ON trading.paper_trading_accounts
    FOR EACH ROW
    WHEN (NEW.is_default = TRUE)
    EXECUTE FUNCTION trading.ensure_single_default_paper_account();

-- Trigger para actualizar cuenta al abrir posicion
CREATE OR REPLACE FUNCTION trading.update_paper_account_on_position_open()
RETURNS TRIGGER AS $$
DECLARE
    v_margin DECIMAL(15, 4);
    v_account RECORD;
BEGIN
    IF NEW.status = 'open' AND (OLD IS NULL OR OLD.status = 'pending') THEN
        SELECT * INTO v_account FROM trading.paper_trading_accounts WHERE id = NEW.account_id;

        -- Calcular margen requerido
        v_margin := (NEW.lot_size * 100000 * NEW.entry_price) / v_account.leverage;
        NEW.margin_used := v_margin;

        -- Actualizar cuenta
        UPDATE trading.paper_trading_accounts
        SET used_margin = used_margin + v_margin,
            free_margin = current_balance - (used_margin + v_margin),
            open_positions_count = open_positions_count + 1,
            margin_level = CASE WHEN (used_margin + v_margin) > 0
                               THEN (equity / (used_margin + v_margin)) * 100
                               ELSE NULL END
        WHERE id = NEW.account_id;
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS paper_position_open ON trading.paper_trading_positions;
CREATE TRIGGER paper_position_open
    BEFORE INSERT OR UPDATE OF status ON trading.paper_trading_positions
    FOR EACH ROW
    EXECUTE FUNCTION trading.update_paper_account_on_position_open();

-- Trigger para actualizar cuenta al cerrar posicion
CREATE OR REPLACE FUNCTION trading.update_paper_account_on_position_close()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.status IN ('closed', 'stopped', 'target_hit') AND OLD.status = 'open' THEN
        NEW.closed_at := NOW();

        -- Calcular P&L
        IF NEW.direction = 'long' THEN
            NEW.profit_loss_pips := (NEW.exit_price - NEW.entry_price) * 10000;
        ELSE
            NEW.profit_loss_pips := (NEW.entry_price - NEW.exit_price) * 10000;
        END IF;
        NEW.profit_loss := NEW.profit_loss_pips * NEW.lot_size * 10;  -- Aproximacion simplificada

        -- Actualizar cuenta
        UPDATE trading.paper_trading_accounts
        SET current_balance = current_balance + COALESCE(NEW.profit_loss, 0),
            used_margin = used_margin - COALESCE(NEW.margin_used, 0),
            free_margin = (current_balance + COALESCE(NEW.profit_loss, 0)) - (used_margin - COALESCE(NEW.margin_used, 0)),
            open_positions_count = open_positions_count - 1,
            total_trades = total_trades + 1,
            winning_trades = winning_trades + CASE WHEN NEW.profit_loss > 0 THEN 1 ELSE 0 END,
            losing_trades = losing_trades + CASE WHEN NEW.profit_loss < 0 THEN 1 ELSE 0 END,
            total_profit = total_profit + CASE WHEN NEW.profit_loss > 0 THEN NEW.profit_loss ELSE 0 END,
            total_loss = total_loss + CASE WHEN NEW.profit_loss < 0 THEN ABS(NEW.profit_loss) ELSE 0 END,
            realized_pnl = realized_pnl + COALESCE(NEW.profit_loss, 0)
        WHERE id = NEW.account_id;

        -- Recalcular estadisticas
        PERFORM trading.recalculate_paper_account_stats(NEW.account_id);
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS paper_position_close ON trading.paper_trading_positions;
CREATE TRIGGER paper_position_close
    BEFORE UPDATE OF status ON trading.paper_trading_positions
    FOR EACH ROW
    EXECUTE FUNCTION trading.update_paper_account_on_position_close();

-- ============================================================================
-- FUNCIONES
-- ============================================================================

-- Funcion para recalcular estadisticas de cuenta
CREATE OR REPLACE FUNCTION trading.recalculate_paper_account_stats(p_account_id UUID)
RETURNS VOID AS $$
DECLARE
    v_account RECORD;
BEGIN
    SELECT * INTO v_account FROM trading.paper_trading_accounts WHERE id = p_account_id;

    UPDATE trading.paper_trading_accounts
    SET win_rate = CASE WHEN total_trades > 0
                        THEN (winning_trades::DECIMAL / total_trades * 100)
                        ELSE 0 END,
        profit_factor = CASE WHEN total_loss > 0
                             THEN total_profit / total_loss
                             ELSE 0 END,
        net_profit = total_profit - total_loss,
        return_percent = CASE WHEN initial_balance > 0
                              THEN ((current_balance - initial_balance) / initial_balance * 100)
                              ELSE 0 END,
        equity = current_balance + unrealized_pnl
    WHERE id = p_account_id;
END;
$$ LANGUAGE plpgsql;

-- Funcion para resetear cuenta paper trading
CREATE OR REPLACE FUNCTION trading.reset_paper_account(
    p_account_id UUID,
    p_new_balance DECIMAL(15, 2) DEFAULT NULL
)
RETURNS BOOLEAN AS $$
DECLARE
    v_account RECORD;
BEGIN
    SELECT * INTO v_account FROM trading.paper_trading_accounts WHERE id = p_account_id;

    IF v_account IS NULL THEN
        RETURN FALSE;
    END IF;

    -- Cerrar todas las posiciones abiertas
    UPDATE trading.paper_trading_positions
    SET status = 'cancelled',
        closed_at = NOW()
    WHERE account_id = p_account_id
      AND status = 'open';

    -- Resetear cuenta
    UPDATE trading.paper_trading_accounts
    SET current_balance = COALESCE(p_new_balance, initial_balance),
        equity = COALESCE(p_new_balance, initial_balance),
        used_margin = 0,
        free_margin = COALESCE(p_new_balance, initial_balance),
        margin_level = NULL,
        unrealized_pnl = 0,
        realized_pnl = 0,
        total_trades = 0,
        winning_trades = 0,
        losing_trades = 0,
        win_rate = 0,
        profit_factor = 0,
        max_drawdown = 0,
        max_drawdown_percent = 0,
        total_profit = 0,
        total_loss = 0,
        net_profit = 0,
        return_percent = 0,
        open_positions_count = 0,
        reset_count = reset_count + 1,
        last_reset_at = NOW()
    WHERE id = p_account_id;

    RETURN TRUE;
END;
$$ LANGUAGE plpgsql;

-- ============================================================================
-- VISTAS
-- ============================================================================

-- Vista de cuentas paper trading
CREATE OR REPLACE VIEW trading.v_paper_accounts AS
SELECT
    pa.id,
    pa.user_id,
    pa.name,
    pa.initial_balance,
    pa.current_balance,
    pa.currency,
    pa.leverage,
    pa.equity,
    pa.unrealized_pnl,
    pa.realized_pnl,
    pa.return_percent,
    pa.win_rate,
    pa.profit_factor,
    pa.total_trades,
    pa.open_positions_count,
    pa.is_default,
    pa.is_active
FROM trading.paper_trading_accounts pa
WHERE pa.is_active = TRUE
ORDER BY pa.is_default DESC, pa.created_at DESC;

-- Vista de posiciones abiertas paper trading
CREATE OR REPLACE VIEW trading.v_paper_open_positions AS
SELECT
    pp.id,
    pp.account_id,
    pa.name AS account_name,
    pp.symbol,
    pp.direction,
    pp.lot_size,
    pp.entry_price,
    pp.current_price,
    pp.stop_loss,
    pp.take_profit,
    pp.unrealized_pnl,
    pp.margin_used,
    pp.opened_at
FROM trading.paper_trading_positions pp
JOIN trading.paper_trading_accounts pa ON pp.account_id = pa.id
WHERE pp.status = 'open'
ORDER BY pp.opened_at DESC;

-- ============================================================================
-- RLS POLICIES
-- ============================================================================

ALTER TABLE trading.paper_trading_accounts ENABLE ROW LEVEL SECURITY;

CREATE POLICY paper_accounts_tenant_isolation ON trading.paper_trading_accounts
    FOR ALL
    USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);

CREATE POLICY paper_accounts_user_isolation ON trading.paper_trading_accounts
    FOR ALL
    USING (user_id = current_setting('app.current_user_id', true)::UUID);

ALTER TABLE trading.paper_trading_positions ENABLE ROW LEVEL SECURITY;

CREATE POLICY paper_positions_tenant_isolation ON trading.paper_trading_positions
    FOR ALL
    USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);

CREATE POLICY paper_positions_user_isolation ON trading.paper_trading_positions
    FOR ALL
    USING (user_id = current_setting('app.current_user_id', true)::UUID);

-- ============================================================================
-- GRANTS
-- ============================================================================

GRANT SELECT, INSERT, UPDATE, DELETE ON trading.paper_trading_accounts TO trading_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON trading.paper_trading_positions TO trading_app;
GRANT SELECT ON trading.paper_trading_accounts TO trading_readonly;
GRANT SELECT ON trading.paper_trading_positions TO trading_readonly;
GRANT SELECT ON trading.v_paper_accounts TO trading_app;
GRANT SELECT ON trading.v_paper_open_positions TO trading_app;
GRANT EXECUTE ON FUNCTION trading.recalculate_paper_account_stats TO trading_app;
GRANT EXECUTE ON FUNCTION trading.reset_paper_account TO trading_app;
