-- ============================================================================
-- SCHEMA: trading
-- TABLE: price_alerts
-- DESCRIPTION: Alertas de precio configuradas por usuarios
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 3 - DDL Implementation Roadmap Q1-2026
-- ============================================================================

-- Enum para tipo de alerta
DO $$ BEGIN
    CREATE TYPE trading.alert_type AS ENUM (
        'price_above',          -- Precio sube por encima de
        'price_below',          -- Precio baja por debajo de
        'price_cross',          -- Precio cruza (cualquier direccion)
        'percent_change',       -- Cambio porcentual
        'volume_spike',         -- Spike de volumen
        'volatility',           -- Alerta de volatilidad
        'indicator',            -- Alerta de indicador tecnico
        'pattern'               -- Patron detectado
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Enum para estado de alerta
DO $$ BEGIN
    CREATE TYPE trading.alert_status AS ENUM (
        'active',               -- Activa y monitoreando
        'triggered',            -- Disparada
        'expired',              -- Expirada sin disparar
        'paused',               -- Pausada por usuario
        'deleted'               -- Eliminada (soft delete)
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Tabla de Alertas de Precio
CREATE TABLE IF NOT EXISTS trading.price_alerts (
    -- 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,
    symbol_id UUID REFERENCES trading.symbols(id),

    -- Simbolo
    symbol VARCHAR(20) NOT NULL,

    -- Tipo y estado
    type trading.alert_type NOT NULL DEFAULT 'price_above',
    status trading.alert_status NOT NULL DEFAULT 'active',

    -- Condicion
    target_price DECIMAL(15, 8),                    -- Precio objetivo
    trigger_value DECIMAL(15, 8),                   -- Valor que dispara (precio, %, etc)
    comparison VARCHAR(10) DEFAULT 'gte',           -- 'gt', 'gte', 'lt', 'lte', 'eq', 'cross'

    -- Para alertas de cambio porcentual
    percent_change DECIMAL(10, 4),
    time_window_minutes INTEGER,                    -- Ventana de tiempo para % change
    base_price DECIMAL(15, 8),                      -- Precio base para calcular %

    -- Para alertas de indicadores
    indicator_name VARCHAR(50),
    indicator_config JSONB,
    indicator_threshold DECIMAL(15, 8),

    -- Configuracion
    is_recurring BOOLEAN NOT NULL DEFAULT FALSE,    -- Se reactiva despues de disparar
    cooldown_minutes INTEGER DEFAULT 60,            -- Cooldown entre disparos
    max_triggers INTEGER,                           -- Max veces que puede disparar (NULL = ilimitado)
    trigger_count INTEGER NOT NULL DEFAULT 0,

    -- Notificacion
    notify_email BOOLEAN NOT NULL DEFAULT TRUE,
    notify_push BOOLEAN NOT NULL DEFAULT TRUE,
    notify_sms BOOLEAN NOT NULL DEFAULT FALSE,
    notification_message TEXT,                      -- Mensaje personalizado

    -- Sonido
    play_sound BOOLEAN NOT NULL DEFAULT TRUE,
    sound_name VARCHAR(50) DEFAULT 'default',

    -- Vigencia
    valid_from TIMESTAMPTZ DEFAULT NOW(),
    valid_until TIMESTAMPTZ,                        -- NULL = sin expiracion

    -- Ultimo trigger
    last_triggered_at TIMESTAMPTZ,
    last_triggered_price DECIMAL(15, 8),

    -- Precio actual (cache)
    current_price DECIMAL(15, 8),
    price_updated_at TIMESTAMPTZ,

    -- Notas
    name VARCHAR(100),                              -- Nombre descriptivo opcional
    notes TEXT,

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

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

COMMENT ON TABLE trading.price_alerts IS
'Alertas de precio configuradas por usuarios para monitorear instrumentos';

COMMENT ON COLUMN trading.price_alerts.is_recurring IS
'Si TRUE, la alerta se reactiva automaticamente despues de disparar';

-- Indices
CREATE INDEX IF NOT EXISTS idx_price_alerts_tenant
    ON trading.price_alerts(tenant_id);

CREATE INDEX IF NOT EXISTS idx_price_alerts_user
    ON trading.price_alerts(user_id);

CREATE INDEX IF NOT EXISTS idx_price_alerts_symbol
    ON trading.price_alerts(symbol);

CREATE INDEX IF NOT EXISTS idx_price_alerts_status
    ON trading.price_alerts(status);

CREATE INDEX IF NOT EXISTS idx_price_alerts_active
    ON trading.price_alerts(symbol, status, target_price)
    WHERE status = 'active';

CREATE INDEX IF NOT EXISTS idx_price_alerts_user_active
    ON trading.price_alerts(user_id, status)
    WHERE status = 'active';

CREATE INDEX IF NOT EXISTS idx_price_alerts_expiring
    ON trading.price_alerts(valid_until)
    WHERE valid_until IS NOT NULL AND status = 'active';

-- Trigger para updated_at
DROP TRIGGER IF EXISTS price_alert_updated_at ON trading.price_alerts;
CREATE TRIGGER price_alert_updated_at
    BEFORE UPDATE ON trading.price_alerts
    FOR EACH ROW
    EXECUTE FUNCTION trading.update_trading_timestamp();

-- Funcion para verificar y disparar alertas
CREATE OR REPLACE FUNCTION trading.check_price_alerts(
    p_symbol VARCHAR(20),
    p_price DECIMAL(15, 8)
)
RETURNS TABLE (
    alert_id UUID,
    user_id UUID,
    alert_type trading.alert_type,
    notification_message TEXT
) AS $$
DECLARE
    v_alert RECORD;
BEGIN
    FOR v_alert IN
        SELECT * FROM trading.price_alerts
        WHERE symbol = p_symbol
          AND status = 'active'
          AND (valid_until IS NULL OR valid_until > NOW())
          AND (last_triggered_at IS NULL OR last_triggered_at + (cooldown_minutes || ' minutes')::INTERVAL < NOW())
    LOOP
        -- Verificar condicion segun tipo
        IF (v_alert.type = 'price_above' AND p_price >= v_alert.target_price) OR
           (v_alert.type = 'price_below' AND p_price <= v_alert.target_price) OR
           (v_alert.type = 'price_cross' AND
            ((v_alert.current_price < v_alert.target_price AND p_price >= v_alert.target_price) OR
             (v_alert.current_price > v_alert.target_price AND p_price <= v_alert.target_price)))
        THEN
            -- Disparar alerta
            UPDATE trading.price_alerts
            SET status = CASE WHEN is_recurring THEN 'active' ELSE 'triggered' END,
                last_triggered_at = NOW(),
                last_triggered_price = p_price,
                trigger_count = trigger_count + 1
            WHERE id = v_alert.id;

            -- Verificar max_triggers
            IF v_alert.max_triggers IS NOT NULL AND v_alert.trigger_count + 1 >= v_alert.max_triggers THEN
                UPDATE trading.price_alerts SET status = 'triggered' WHERE id = v_alert.id;
            END IF;

            RETURN QUERY SELECT
                v_alert.id,
                v_alert.user_id,
                v_alert.type,
                COALESCE(v_alert.notification_message,
                    v_alert.symbol || ' alcanzó ' || p_price::TEXT);
        END IF;
    END LOOP;

    -- Actualizar precio actual en todas las alertas del simbolo
    UPDATE trading.price_alerts
    SET current_price = p_price,
        price_updated_at = NOW()
    WHERE symbol = p_symbol
      AND status = 'active';
END;
$$ LANGUAGE plpgsql;

-- Funcion para expirar alertas
CREATE OR REPLACE FUNCTION trading.expire_old_alerts()
RETURNS INTEGER AS $$
DECLARE
    v_count INTEGER;
BEGIN
    UPDATE trading.price_alerts
    SET status = 'expired'
    WHERE status = 'active'
      AND valid_until IS NOT NULL
      AND valid_until < NOW();

    GET DIAGNOSTICS v_count = ROW_COUNT;
    RETURN v_count;
END;
$$ LANGUAGE plpgsql;

-- Vista de alertas activas del usuario
CREATE OR REPLACE VIEW trading.v_my_price_alerts AS
SELECT
    id,
    symbol,
    type,
    status,
    target_price,
    current_price,
    name,
    notify_email,
    notify_push,
    is_recurring,
    trigger_count,
    last_triggered_at,
    valid_until,
    created_at
FROM trading.price_alerts
WHERE status IN ('active', 'paused')
ORDER BY created_at DESC;

-- RLS Policy para multi-tenancy
ALTER TABLE trading.price_alerts ENABLE ROW LEVEL SECURITY;

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

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

-- Grants
GRANT SELECT, INSERT, UPDATE, DELETE ON trading.price_alerts TO trading_app;
GRANT SELECT ON trading.price_alerts TO trading_readonly;
GRANT SELECT ON trading.v_my_price_alerts TO trading_app;
GRANT EXECUTE ON FUNCTION trading.check_price_alerts TO trading_app;
GRANT EXECUTE ON FUNCTION trading.expire_old_alerts TO trading_app;
