-- ============================================================================
-- SCHEMA: ml
-- TABLE: predictions
-- DESCRIPTION: Tabla de predicciones individuales generadas
-- VERSION: 1.0.0
-- CREATED: 2026-01-10
-- DEPENDS: ml.prediction_purchases
-- ============================================================================

-- Enum para tipo de prediccion
DO $$ BEGIN
    CREATE TYPE ml.prediction_type AS ENUM (
        'AMD',              -- Accumulation, Manipulation, Distribution
        'RANGE',            -- Range prediction
        'TPSL',             -- Take Profit / Stop Loss
        'ICT_SMC',          -- ICT Smart Money Concepts
        'STRATEGY_ENSEMBLE' -- Combined strategies
    );
EXCEPTION WHEN duplicate_object THEN null; END $$;

-- Enum para clase de activo
DO $$ BEGIN
    CREATE TYPE ml.asset_class AS ENUM (
        'FOREX',
        'CRYPTO',
        'INDICES',
        'COMMODITIES'
    );
EXCEPTION WHEN duplicate_object THEN null; END $$;

-- Enum para estado de prediccion
DO $$ BEGIN
    CREATE TYPE ml.prediction_status AS ENUM (
        'pending',      -- Generada, esperando entrega
        'delivered',    -- Entregada al usuario
        'expired',      -- Expirada sin validar
        'validated',    -- Validada con resultado
        'invalidated'   -- Invalidada por error
    );
EXCEPTION WHEN duplicate_object THEN null; END $$;

-- Tabla de predicciones individuales
CREATE TABLE IF NOT EXISTS ml.predictions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,
    user_id UUID NOT NULL,

    -- Referencia a la compra
    purchase_id UUID NOT NULL REFERENCES ml.prediction_purchases(id),

    -- Tipo y activo
    prediction_type ml.prediction_type NOT NULL,
    asset VARCHAR(20) NOT NULL,
    asset_class ml.asset_class NOT NULL,
    timeframe VARCHAR(10) NOT NULL,

    -- Predicción
    direction VARCHAR(10) NOT NULL CHECK (direction IN ('LONG', 'SHORT', 'NEUTRAL')),
    entry_price DECIMAL(20, 8),
    target_price DECIMAL(20, 8),
    stop_loss DECIMAL(20, 8),
    confidence DECIMAL(5, 4) CHECK (confidence >= 0 AND confidence <= 1),

    -- Estado
    status ml.prediction_status NOT NULL DEFAULT 'pending',
    expires_at TIMESTAMPTZ NOT NULL,
    delivered_at TIMESTAMPTZ,

    -- Datos adicionales
    prediction_data JSONB DEFAULT '{}',
    metadata JSONB DEFAULT '{}',
    created_at TIMESTAMPTZ DEFAULT NOW()
);

COMMENT ON TABLE ml.predictions IS
'Predicciones individuales generadas por modelos ML';

COMMENT ON COLUMN ml.predictions.prediction_type IS 'Tipo de modelo usado para la prediccion';
COMMENT ON COLUMN ml.predictions.asset_class IS 'Clase de activo (FOREX, CRYPTO, etc.)';
COMMENT ON COLUMN ml.predictions.status IS 'Estado actual de la prediccion';
COMMENT ON COLUMN ml.predictions.confidence IS 'Nivel de confianza del modelo (0-1)';

-- Indices
CREATE INDEX IF NOT EXISTS idx_pred_tenant ON ml.predictions(tenant_id);
CREATE INDEX IF NOT EXISTS idx_pred_user ON ml.predictions(user_id);
CREATE INDEX IF NOT EXISTS idx_pred_purchase ON ml.predictions(purchase_id);
CREATE INDEX IF NOT EXISTS idx_pred_status ON ml.predictions(status);
CREATE INDEX IF NOT EXISTS idx_pred_type ON ml.predictions(prediction_type);
CREATE INDEX IF NOT EXISTS idx_pred_asset ON ml.predictions(asset);
CREATE INDEX IF NOT EXISTS idx_pred_asset_class ON ml.predictions(asset_class);
CREATE INDEX IF NOT EXISTS idx_pred_created ON ml.predictions(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_pred_expires ON ml.predictions(expires_at)
    WHERE status = 'pending';

-- Indice compuesto para busquedas comunes
CREATE INDEX IF NOT EXISTS idx_pred_user_status ON ml.predictions(user_id, status, created_at DESC);

-- RLS
ALTER TABLE ml.predictions ENABLE ROW LEVEL SECURITY;

CREATE POLICY pred_tenant ON ml.predictions
    FOR ALL
    USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);

-- Grants
GRANT SELECT, INSERT, UPDATE ON ml.predictions TO trading_app;
GRANT SELECT ON ml.predictions TO trading_readonly;

-- Actualizar FK en prediction_outcomes para referenciar predictions
DO $$
BEGIN
    ALTER TABLE ml.prediction_outcomes
        DROP CONSTRAINT IF EXISTS prediction_outcomes_prediction_id_fkey;
EXCEPTION WHEN undefined_object THEN null;
END $$;

ALTER TABLE ml.prediction_outcomes
    ADD CONSTRAINT prediction_outcomes_prediction_id_fkey
    FOREIGN KEY (prediction_id) REFERENCES ml.predictions(id)
    ON DELETE SET NULL;

-- Funcion para generar prediccion desde un purchase
CREATE OR REPLACE FUNCTION ml.generate_prediction(
    p_purchase_id UUID,
    p_prediction_type ml.prediction_type,
    p_asset VARCHAR(20),
    p_asset_class ml.asset_class,
    p_timeframe VARCHAR(10),
    p_direction VARCHAR(10),
    p_entry DECIMAL(20, 8) DEFAULT NULL,
    p_target DECIMAL(20, 8) DEFAULT NULL,
    p_sl DECIMAL(20, 8) DEFAULT NULL,
    p_confidence DECIMAL(5, 4) DEFAULT 0.5,
    p_expires_hours INT DEFAULT 24,
    p_prediction_data JSONB DEFAULT '{}'
)
RETURNS UUID AS $$
DECLARE
    v_purchase ml.prediction_purchases%ROWTYPE;
    v_prediction_id UUID;
BEGIN
    -- Validar purchase
    SELECT * INTO v_purchase
    FROM ml.prediction_purchases
    WHERE id = p_purchase_id
    FOR UPDATE;

    IF NOT FOUND THEN
        RAISE EXCEPTION 'Purchase not found: %', p_purchase_id;
    END IF;

    IF NOT v_purchase.is_active THEN
        RAISE EXCEPTION 'Purchase is not active';
    END IF;

    IF v_purchase.valid_until IS NOT NULL AND v_purchase.valid_until < NOW() THEN
        RAISE EXCEPTION 'Purchase has expired';
    END IF;

    IF v_purchase.predictions_used >= v_purchase.predictions_total THEN
        RAISE EXCEPTION 'No predictions remaining';
    END IF;

    -- Crear prediccion
    INSERT INTO ml.predictions (
        tenant_id, user_id, purchase_id,
        prediction_type, asset, asset_class, timeframe,
        direction, entry_price, target_price, stop_loss, confidence,
        expires_at, prediction_data
    ) VALUES (
        v_purchase.tenant_id, v_purchase.user_id, p_purchase_id,
        p_prediction_type, p_asset, p_asset_class, p_timeframe,
        p_direction, p_entry, p_target, p_sl, p_confidence,
        NOW() + (p_expires_hours || ' hours')::INTERVAL,
        p_prediction_data
    ) RETURNING id INTO v_prediction_id;

    -- Incrementar contador
    UPDATE ml.prediction_purchases
    SET predictions_used = predictions_used + 1
    WHERE id = p_purchase_id;

    RETURN v_prediction_id;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION ml.generate_prediction IS
'Genera una nueva prediccion desde un purchase, validando limites y estado';
