-- ============================================================================
-- SCHEMA: data_sources
-- TABLE: ticker_mapping
-- DESCRIPTION: Mapeo de simbolos entre proveedores y sistema interno
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 4 - DDL Implementation Roadmap Q1-2026
-- ============================================================================

-- Tabla de Mapeo de Tickers
CREATE TABLE IF NOT EXISTS data_sources.ticker_mapping (
    -- Identificadores
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,

    -- Referencias
    provider_id UUID NOT NULL REFERENCES data_sources.api_providers(id) ON DELETE CASCADE,
    ticker_id UUID REFERENCES market_data.tickers(id),
    symbol_id UUID REFERENCES trading.symbols(id),

    -- Simbolos
    internal_symbol VARCHAR(20) NOT NULL,               -- Simbolo interno: EURUSD
    provider_symbol VARCHAR(50) NOT NULL,               -- Simbolo del provider: EUR/USD, EURUSD=X
    provider_code VARCHAR(50) NOT NULL,                 -- Codigo del proveedor

    -- Tipo de activo
    asset_class VARCHAR(30) NOT NULL,                   -- 'forex', 'crypto', 'stock', 'index', 'commodity'
    instrument_type VARCHAR(30),                        -- 'spot', 'future', 'option', 'cfd'

    -- Configuracion de precision
    price_precision INTEGER NOT NULL DEFAULT 5,         -- Decimales de precio
    lot_precision INTEGER NOT NULL DEFAULT 2,           -- Decimales de lote
    min_lot_size DECIMAL(10, 4) DEFAULT 0.01,
    max_lot_size DECIMAL(10, 4) DEFAULT 100,
    lot_step DECIMAL(10, 4) DEFAULT 0.01,

    -- Factor de ajuste
    price_multiplier DECIMAL(10, 6) DEFAULT 1.0,        -- Para convertir precio del provider
    inverse_quote BOOLEAN NOT NULL DEFAULT FALSE,       -- Si el provider da inverso

    -- Monedas
    base_currency VARCHAR(10),                          -- EUR
    quote_currency VARCHAR(10),                         -- USD

    -- Estado
    is_active BOOLEAN NOT NULL DEFAULT TRUE,
    is_verified BOOLEAN NOT NULL DEFAULT FALSE,         -- Verificado manualmente

    -- Horarios de trading
    trading_hours JSONB,                                -- [{"day": 0, "open": "00:00", "close": "23:59"}]
    timezone VARCHAR(50) DEFAULT 'UTC',

    -- Estadisticas de uso
    last_data_at TIMESTAMPTZ,
    data_points_count BIGINT NOT NULL DEFAULT 0,
    error_count INTEGER NOT NULL DEFAULT 0,
    last_error TEXT,

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

    -- Timestamps
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    verified_at TIMESTAMPTZ,
    verified_by UUID REFERENCES users.users(id),

    -- Constraints
    CONSTRAINT ticker_mapping_unique UNIQUE (tenant_id, provider_id, internal_symbol),
    CONSTRAINT ticker_mapping_provider_symbol_unique UNIQUE (provider_id, provider_symbol)
);

COMMENT ON TABLE data_sources.ticker_mapping IS
'Mapeo de simbolos entre proveedores externos y nomenclatura interna';

COMMENT ON COLUMN data_sources.ticker_mapping.price_multiplier IS
'Factor para convertir precio del proveedor a precio interno. Ej: 1.0 para EURUSD, 0.01 para JPY pairs';

COMMENT ON COLUMN data_sources.ticker_mapping.inverse_quote IS
'TRUE si el proveedor reporta USD/EUR en lugar de EUR/USD';

-- Indices
CREATE INDEX IF NOT EXISTS idx_ticker_mapping_tenant
    ON data_sources.ticker_mapping(tenant_id);

CREATE INDEX IF NOT EXISTS idx_ticker_mapping_provider
    ON data_sources.ticker_mapping(provider_id);

CREATE INDEX IF NOT EXISTS idx_ticker_mapping_internal
    ON data_sources.ticker_mapping(internal_symbol);

CREATE INDEX IF NOT EXISTS idx_ticker_mapping_provider_symbol
    ON data_sources.ticker_mapping(provider_symbol);

CREATE INDEX IF NOT EXISTS idx_ticker_mapping_active
    ON data_sources.ticker_mapping(provider_id, is_active)
    WHERE is_active = TRUE;

CREATE INDEX IF NOT EXISTS idx_ticker_mapping_asset
    ON data_sources.ticker_mapping(asset_class);

CREATE INDEX IF NOT EXISTS idx_ticker_mapping_ticker
    ON data_sources.ticker_mapping(ticker_id)
    WHERE ticker_id IS NOT NULL;

-- Trigger para updated_at
DROP TRIGGER IF EXISTS ticker_mapping_updated_at ON data_sources.ticker_mapping;
CREATE TRIGGER ticker_mapping_updated_at
    BEFORE UPDATE ON data_sources.ticker_mapping
    FOR EACH ROW
    EXECUTE FUNCTION data_sources.update_timestamp();

-- Funcion para obtener simbolo del proveedor
CREATE OR REPLACE FUNCTION data_sources.get_provider_symbol(
    p_tenant_id UUID,
    p_provider_code VARCHAR,
    p_internal_symbol VARCHAR
)
RETURNS data_sources.ticker_mapping AS $$
DECLARE
    v_mapping data_sources.ticker_mapping;
BEGIN
    SELECT tm.* INTO v_mapping
    FROM data_sources.ticker_mapping tm
    JOIN data_sources.api_providers ap ON tm.provider_id = ap.id
    WHERE tm.tenant_id = p_tenant_id
      AND ap.code = p_provider_code
      AND tm.internal_symbol = p_internal_symbol
      AND tm.is_active = TRUE;

    RETURN v_mapping;
END;
$$ LANGUAGE plpgsql;

-- Funcion para convertir precio del proveedor a interno
CREATE OR REPLACE FUNCTION data_sources.convert_provider_price(
    p_mapping_id UUID,
    p_provider_price DECIMAL
)
RETURNS DECIMAL AS $$
DECLARE
    v_mapping data_sources.ticker_mapping;
    v_converted DECIMAL;
BEGIN
    SELECT * INTO v_mapping FROM data_sources.ticker_mapping WHERE id = p_mapping_id;

    IF v_mapping IS NULL THEN
        RETURN p_provider_price;
    END IF;

    v_converted := p_provider_price * v_mapping.price_multiplier;

    IF v_mapping.inverse_quote THEN
        v_converted := 1.0 / v_converted;
    END IF;

    RETURN ROUND(v_converted, v_mapping.price_precision);
END;
$$ LANGUAGE plpgsql;

-- Vista de mappings activos por proveedor
CREATE OR REPLACE VIEW data_sources.v_ticker_mappings AS
SELECT
    tm.id,
    tm.tenant_id,
    ap.name AS provider_name,
    ap.code AS provider_code,
    tm.internal_symbol,
    tm.provider_symbol,
    tm.asset_class,
    tm.price_precision,
    tm.price_multiplier,
    tm.inverse_quote,
    tm.is_active,
    tm.is_verified,
    tm.last_data_at,
    tm.data_points_count
FROM data_sources.ticker_mapping tm
JOIN data_sources.api_providers ap ON tm.provider_id = ap.id
WHERE tm.is_active = TRUE
ORDER BY tm.internal_symbol, ap.priority;

-- RLS Policies
ALTER TABLE data_sources.ticker_mapping ENABLE ROW LEVEL SECURITY;

CREATE POLICY ticker_mapping_tenant_isolation ON data_sources.ticker_mapping
    FOR ALL
    USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);

-- Grants
GRANT SELECT, INSERT, UPDATE, DELETE ON data_sources.ticker_mapping TO trading_app;
GRANT SELECT ON data_sources.ticker_mapping TO trading_readonly;
GRANT SELECT ON data_sources.v_ticker_mappings TO trading_app;
GRANT EXECUTE ON FUNCTION data_sources.get_provider_symbol TO trading_app;
GRANT EXECUTE ON FUNCTION data_sources.convert_provider_price TO trading_app;
