-- ============================================================================
-- SCHEMA: data_sources
-- TABLE: api_providers
-- DESCRIPTION: Proveedores de datos de mercado (Polygon, Alpha Vantage, etc.)
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 4 - DDL Implementation Roadmap Q1-2026
-- ============================================================================

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

COMMENT ON SCHEMA data_sources IS
'Fuentes de datos de mercado y proveedores de API';

-- Enum para tipo de proveedor
DO $$ BEGIN
    CREATE TYPE data_sources.provider_type AS ENUM (
        'market_data',          -- Datos de mercado (OHLCV, quotes)
        'news',                 -- Noticias financieras
        'fundamental',          -- Datos fundamentales
        'sentiment',            -- Analisis de sentimiento
        'economic_calendar',    -- Calendario economico
        'alternative'           -- Datos alternativos
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Enum para estado del proveedor
DO $$ BEGIN
    CREATE TYPE data_sources.provider_status AS ENUM (
        'active',               -- Activo y funcionando
        'inactive',             -- Inactivo temporalmente
        'degraded',             -- Funcionando con problemas
        'rate_limited',         -- Limitado por rate limit
        'maintenance',          -- En mantenimiento
        'error',                -- Con errores
        'deprecated'            -- Deprecado
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Enum para frecuencia de datos
DO $$ BEGIN
    CREATE TYPE data_sources.data_frequency AS ENUM (
        'tick',                 -- Tick por tick
        'second',               -- Por segundo
        'minute',               -- Por minuto
        'five_minutes',         -- Cada 5 minutos
        'fifteen_minutes',      -- Cada 15 minutos
        'hourly',               -- Cada hora
        'daily',                -- Diario
        'weekly',               -- Semanal
        'monthly'               -- Mensual
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

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

    -- Informacion del proveedor
    name VARCHAR(100) NOT NULL,
    code VARCHAR(50) NOT NULL,                          -- 'polygon', 'alpha_vantage', 'binance'
    type data_sources.provider_type NOT NULL,
    status data_sources.provider_status NOT NULL DEFAULT 'inactive',

    -- Configuracion de API
    base_url TEXT NOT NULL,
    api_version VARCHAR(20),
    auth_type VARCHAR(50) NOT NULL DEFAULT 'api_key',   -- 'api_key', 'oauth2', 'basic', 'none'

    -- Credenciales (encriptadas)
    api_key_encrypted TEXT,                             -- Encriptado con pgcrypto
    api_secret_encrypted TEXT,
    additional_credentials JSONB DEFAULT '{}'::JSONB,   -- Otras credenciales encriptadas

    -- Rate Limiting
    rate_limit_requests INTEGER,                        -- Requests por periodo
    rate_limit_period_seconds INTEGER DEFAULT 60,
    current_request_count INTEGER NOT NULL DEFAULT 0,
    rate_limit_reset_at TIMESTAMPTZ,
    burst_limit INTEGER,                                -- Limite de burst

    -- Capacidades
    supported_symbols JSONB DEFAULT '[]'::JSONB,        -- Simbolos soportados
    supported_frequencies data_sources.data_frequency[],
    supports_realtime BOOLEAN NOT NULL DEFAULT FALSE,
    supports_historical BOOLEAN NOT NULL DEFAULT TRUE,
    max_historical_days INTEGER,                        -- Dias maximos de historico

    -- Cobertura
    asset_classes VARCHAR(50)[],                        -- ['forex', 'stocks', 'crypto']
    exchanges VARCHAR(50)[],                            -- ['NYSE', 'NASDAQ', 'BINANCE']
    regions VARCHAR(50)[],                              -- ['US', 'EU', 'LATAM']

    -- Costos
    cost_type VARCHAR(20) DEFAULT 'free',               -- 'free', 'paid', 'freemium'
    monthly_cost DECIMAL(10, 2),
    cost_per_request DECIMAL(10, 6),
    free_tier_limit INTEGER,                            -- Requests gratis por mes

    -- Prioridad y fallback
    priority INTEGER NOT NULL DEFAULT 100,              -- Menor = mayor prioridad
    fallback_provider_id UUID REFERENCES data_sources.api_providers(id),
    is_primary BOOLEAN NOT NULL DEFAULT FALSE,

    -- Calidad de datos
    data_quality_score DECIMAL(5, 2),                   -- 0-100
    latency_avg_ms INTEGER,
    uptime_percent DECIMAL(5, 2),

    -- Health check
    health_check_url TEXT,
    last_health_check_at TIMESTAMPTZ,
    last_health_status VARCHAR(20),
    consecutive_failures INTEGER NOT NULL DEFAULT 0,

    -- Estadisticas de uso
    total_requests INTEGER NOT NULL DEFAULT 0,
    successful_requests INTEGER NOT NULL DEFAULT 0,
    failed_requests INTEGER NOT NULL DEFAULT 0,
    last_request_at TIMESTAMPTZ,
    last_error TEXT,
    last_error_at TIMESTAMPTZ,

    -- Documentacion
    documentation_url TEXT,
    terms_of_service_url TEXT,
    support_email VARCHAR(255),

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

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

    -- Constraints
    CONSTRAINT api_providers_code_unique UNIQUE (tenant_id, code),
    CONSTRAINT api_providers_rate_check CHECK (rate_limit_requests IS NULL OR rate_limit_requests > 0)
);

COMMENT ON TABLE data_sources.api_providers IS
'Proveedores de datos de mercado configurados por tenant';

COMMENT ON COLUMN data_sources.api_providers.api_key_encrypted IS
'API key encriptada - usar pgcrypto para encrypt/decrypt';

COMMENT ON COLUMN data_sources.api_providers.priority IS
'Prioridad de uso - menor numero = mayor prioridad';

-- Indices
CREATE INDEX IF NOT EXISTS idx_api_providers_tenant
    ON data_sources.api_providers(tenant_id);

CREATE INDEX IF NOT EXISTS idx_api_providers_code
    ON data_sources.api_providers(code);

CREATE INDEX IF NOT EXISTS idx_api_providers_status
    ON data_sources.api_providers(status);

CREATE INDEX IF NOT EXISTS idx_api_providers_type
    ON data_sources.api_providers(type);

CREATE INDEX IF NOT EXISTS idx_api_providers_active
    ON data_sources.api_providers(tenant_id, status, priority)
    WHERE status = 'active';

CREATE INDEX IF NOT EXISTS idx_api_providers_primary
    ON data_sources.api_providers(tenant_id, is_primary)
    WHERE is_primary = TRUE;

-- GIN index para busqueda en asset_classes y exchanges
CREATE INDEX IF NOT EXISTS idx_api_providers_assets_gin
    ON data_sources.api_providers USING GIN (asset_classes);

-- Funcion de timestamp
CREATE OR REPLACE FUNCTION data_sources.update_timestamp()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at := NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

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

-- Trigger para asegurar solo un proveedor primario por tipo
CREATE OR REPLACE FUNCTION data_sources.ensure_single_primary_provider()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.is_primary = TRUE THEN
        UPDATE data_sources.api_providers
        SET is_primary = FALSE
        WHERE tenant_id = NEW.tenant_id
          AND type = NEW.type
          AND id != NEW.id
          AND is_primary = TRUE;
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS api_provider_single_primary ON data_sources.api_providers;
CREATE TRIGGER api_provider_single_primary
    BEFORE INSERT OR UPDATE OF is_primary ON data_sources.api_providers
    FOR EACH ROW
    WHEN (NEW.is_primary = TRUE)
    EXECUTE FUNCTION data_sources.ensure_single_primary_provider();

-- Funcion para obtener proveedor activo por tipo
CREATE OR REPLACE FUNCTION data_sources.get_active_provider(
    p_tenant_id UUID,
    p_type data_sources.provider_type,
    p_symbol VARCHAR DEFAULT NULL
)
RETURNS data_sources.api_providers AS $$
DECLARE
    v_provider data_sources.api_providers;
BEGIN
    SELECT * INTO v_provider
    FROM data_sources.api_providers
    WHERE tenant_id = p_tenant_id
      AND type = p_type
      AND status = 'active'
      AND (p_symbol IS NULL OR p_symbol = ANY(
          SELECT jsonb_array_elements_text(supported_symbols)
      ))
    ORDER BY is_primary DESC, priority ASC
    LIMIT 1;

    RETURN v_provider;
END;
$$ LANGUAGE plpgsql;

-- Funcion para incrementar contador de requests
CREATE OR REPLACE FUNCTION data_sources.increment_request_count(
    p_provider_id UUID,
    p_success BOOLEAN DEFAULT TRUE
)
RETURNS VOID AS $$
BEGIN
    UPDATE data_sources.api_providers
    SET total_requests = total_requests + 1,
        successful_requests = successful_requests + CASE WHEN p_success THEN 1 ELSE 0 END,
        failed_requests = failed_requests + CASE WHEN NOT p_success THEN 1 ELSE 0 END,
        current_request_count = current_request_count + 1,
        last_request_at = NOW(),
        consecutive_failures = CASE WHEN p_success THEN 0 ELSE consecutive_failures + 1 END
    WHERE id = p_provider_id;
END;
$$ LANGUAGE plpgsql;

-- Vista de proveedores activos
CREATE OR REPLACE VIEW data_sources.v_active_providers AS
SELECT
    id,
    tenant_id,
    name,
    code,
    type,
    status,
    is_primary,
    priority,
    rate_limit_requests,
    current_request_count,
    supports_realtime,
    asset_classes,
    data_quality_score,
    uptime_percent,
    total_requests,
    last_request_at
FROM data_sources.api_providers
WHERE status = 'active'
ORDER BY is_primary DESC, priority ASC;

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

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

-- Grants
GRANT USAGE ON SCHEMA data_sources TO trading_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON data_sources.api_providers TO trading_app;
GRANT SELECT ON data_sources.api_providers TO trading_readonly;
GRANT SELECT ON data_sources.v_active_providers TO trading_app;
GRANT EXECUTE ON FUNCTION data_sources.get_active_provider TO trading_app;
GRANT EXECUTE ON FUNCTION data_sources.increment_request_count TO trading_app;
