-- ============================================================================
-- SCHEMA: admin
-- TABLE: platform_analytics
-- DESCRIPTION: Metricas agregadas de la plataforma
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 1 - DDL Implementation Roadmap Q1-2026
-- ============================================================================

-- Enum para tipo de metrica
DO $$ BEGIN
    CREATE TYPE admin.metric_type AS ENUM (
        'counter',          -- Contador simple
        'gauge',            -- Valor actual
        'histogram',        -- Distribucion
        'summary',          -- Resumen estadistico
        'rate'              -- Tasa por periodo
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Enum para granularidad
DO $$ BEGIN
    CREATE TYPE admin.time_granularity AS ENUM (
        'minute',
        'hour',
        'day',
        'week',
        'month',
        'quarter',
        'year'
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Tabla de Metricas de Plataforma
CREATE TABLE IF NOT EXISTS admin.platform_analytics (
    -- Identificadores
    id BIGSERIAL PRIMARY KEY,

    -- Scope
    tenant_id UUID REFERENCES tenants.tenants(id),  -- NULL = plataforma global

    -- Metrica
    metric_name VARCHAR(100) NOT NULL,
    metric_type admin.metric_type NOT NULL DEFAULT 'gauge',
    category VARCHAR(50) NOT NULL,                  -- 'users', 'trading', 'revenue', 'performance'

    -- Periodo
    period_start TIMESTAMPTZ NOT NULL,
    period_end TIMESTAMPTZ NOT NULL,
    granularity admin.time_granularity NOT NULL DEFAULT 'day',

    -- Valores
    value DECIMAL(20, 6) NOT NULL,
    previous_value DECIMAL(20, 6),                  -- Valor periodo anterior (para calcular delta)
    delta_value DECIMAL(20, 6),                     -- Cambio vs periodo anterior
    delta_percent DECIMAL(10, 4),                   -- Cambio porcentual

    -- Estadisticas (para histogramas/summaries)
    min_value DECIMAL(20, 6),
    max_value DECIMAL(20, 6),
    avg_value DECIMAL(20, 6),
    median_value DECIMAL(20, 6),
    p95_value DECIMAL(20, 6),
    p99_value DECIMAL(20, 6),
    stddev_value DECIMAL(20, 6),
    sample_count BIGINT,

    -- Dimensiones adicionales
    dimensions JSONB DEFAULT '{}'::JSONB,           -- Dimensiones para desglose

    -- Metadata
    source VARCHAR(50) NOT NULL DEFAULT 'system',   -- Origen del dato
    is_estimated BOOLEAN NOT NULL DEFAULT FALSE,    -- Dato estimado vs real
    confidence_level DECIMAL(5, 4),                 -- Nivel de confianza (0-1)

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

    -- Constraints
    CONSTRAINT analytics_period_check CHECK (period_end > period_start),
    CONSTRAINT analytics_unique_metric UNIQUE (tenant_id, metric_name, granularity, period_start)
);

COMMENT ON TABLE admin.platform_analytics IS
'Metricas agregadas de la plataforma para dashboards y reportes';

COMMENT ON COLUMN admin.platform_analytics.tenant_id IS
'NULL para metricas globales de plataforma, UUID para metricas por tenant';

COMMENT ON COLUMN admin.platform_analytics.dimensions IS
'Dimensiones adicionales como {"country": "MX", "plan": "premium"}';

-- Particionamiento por fecha (recomendado para produccion)
-- CREATE TABLE admin.platform_analytics_partitioned (
--     LIKE admin.platform_analytics INCLUDING ALL
-- ) PARTITION BY RANGE (period_start);

-- Indices
CREATE INDEX IF NOT EXISTS idx_analytics_metric_period
    ON admin.platform_analytics(metric_name, period_start DESC);

CREATE INDEX IF NOT EXISTS idx_analytics_tenant_metric
    ON admin.platform_analytics(tenant_id, metric_name, period_start DESC)
    WHERE tenant_id IS NOT NULL;

CREATE INDEX IF NOT EXISTS idx_analytics_category
    ON admin.platform_analytics(category, period_start DESC);

CREATE INDEX IF NOT EXISTS idx_analytics_granularity
    ON admin.platform_analytics(granularity, period_start DESC);

CREATE INDEX IF NOT EXISTS idx_analytics_global
    ON admin.platform_analytics(metric_name, granularity, period_start DESC)
    WHERE tenant_id IS NULL;

-- GIN index para dimensiones
CREATE INDEX IF NOT EXISTS idx_analytics_dimensions_gin
    ON admin.platform_analytics USING GIN (dimensions);

-- Trigger para calcular deltas automaticamente
CREATE OR REPLACE FUNCTION admin.calculate_analytics_delta()
RETURNS TRIGGER AS $$
DECLARE
    v_prev RECORD;
    v_period_interval INTERVAL;
BEGIN
    -- Calcular intervalo basado en granularidad
    CASE NEW.granularity
        WHEN 'minute' THEN v_period_interval := INTERVAL '1 minute';
        WHEN 'hour' THEN v_period_interval := INTERVAL '1 hour';
        WHEN 'day' THEN v_period_interval := INTERVAL '1 day';
        WHEN 'week' THEN v_period_interval := INTERVAL '1 week';
        WHEN 'month' THEN v_period_interval := INTERVAL '1 month';
        WHEN 'quarter' THEN v_period_interval := INTERVAL '3 months';
        WHEN 'year' THEN v_period_interval := INTERVAL '1 year';
    END CASE;

    -- Buscar valor anterior
    SELECT value INTO v_prev
    FROM admin.platform_analytics
    WHERE metric_name = NEW.metric_name
      AND granularity = NEW.granularity
      AND (tenant_id = NEW.tenant_id OR (tenant_id IS NULL AND NEW.tenant_id IS NULL))
      AND period_start = NEW.period_start - v_period_interval
    LIMIT 1;

    IF v_prev IS NOT NULL THEN
        NEW.previous_value := v_prev.value;
        NEW.delta_value := NEW.value - v_prev.value;
        IF v_prev.value != 0 THEN
            NEW.delta_percent := ((NEW.value - v_prev.value) / v_prev.value) * 100;
        END IF;
    END IF;

    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS analytics_calc_delta ON admin.platform_analytics;
CREATE TRIGGER analytics_calc_delta
    BEFORE INSERT ON admin.platform_analytics
    FOR EACH ROW
    EXECUTE FUNCTION admin.calculate_analytics_delta();

-- Funcion para insertar o actualizar metrica
CREATE OR REPLACE FUNCTION admin.upsert_metric(
    p_metric_name VARCHAR(100),
    p_category VARCHAR(50),
    p_value DECIMAL(20, 6),
    p_granularity admin.time_granularity DEFAULT 'day',
    p_tenant_id UUID DEFAULT NULL,
    p_metric_type admin.metric_type DEFAULT 'gauge',
    p_dimensions JSONB DEFAULT '{}'::JSONB
)
RETURNS BIGINT AS $$
DECLARE
    v_period_start TIMESTAMPTZ;
    v_period_end TIMESTAMPTZ;
    v_id BIGINT;
BEGIN
    -- Calcular periodo basado en granularidad
    CASE p_granularity
        WHEN 'minute' THEN
            v_period_start := DATE_TRUNC('minute', NOW());
            v_period_end := v_period_start + INTERVAL '1 minute';
        WHEN 'hour' THEN
            v_period_start := DATE_TRUNC('hour', NOW());
            v_period_end := v_period_start + INTERVAL '1 hour';
        WHEN 'day' THEN
            v_period_start := DATE_TRUNC('day', NOW());
            v_period_end := v_period_start + INTERVAL '1 day';
        WHEN 'week' THEN
            v_period_start := DATE_TRUNC('week', NOW());
            v_period_end := v_period_start + INTERVAL '1 week';
        WHEN 'month' THEN
            v_period_start := DATE_TRUNC('month', NOW());
            v_period_end := v_period_start + INTERVAL '1 month';
        WHEN 'quarter' THEN
            v_period_start := DATE_TRUNC('quarter', NOW());
            v_period_end := v_period_start + INTERVAL '3 months';
        WHEN 'year' THEN
            v_period_start := DATE_TRUNC('year', NOW());
            v_period_end := v_period_start + INTERVAL '1 year';
    END CASE;

    INSERT INTO admin.platform_analytics (
        tenant_id, metric_name, metric_type, category,
        period_start, period_end, granularity,
        value, dimensions
    ) VALUES (
        p_tenant_id, p_metric_name, p_metric_type, p_category,
        v_period_start, v_period_end, p_granularity,
        p_value, p_dimensions
    )
    ON CONFLICT (tenant_id, metric_name, granularity, period_start)
    DO UPDATE SET
        value = EXCLUDED.value,
        dimensions = EXCLUDED.dimensions,
        collected_at = NOW()
    RETURNING id INTO v_id;

    RETURN v_id;
END;
$$ LANGUAGE plpgsql;

-- Vista de metricas recientes
CREATE OR REPLACE VIEW admin.v_recent_metrics AS
SELECT
    metric_name,
    category,
    granularity,
    tenant_id,
    value,
    delta_percent,
    period_start,
    collected_at
FROM admin.platform_analytics
WHERE collected_at > NOW() - INTERVAL '24 hours'
ORDER BY metric_name, period_start DESC;

-- Vista de KPIs principales
CREATE OR REPLACE VIEW admin.v_platform_kpis AS
SELECT
    metric_name,
    value AS current_value,
    previous_value,
    delta_percent AS change_percent,
    period_start,
    granularity
FROM admin.platform_analytics
WHERE tenant_id IS NULL  -- Solo metricas globales
  AND granularity = 'day'
  AND metric_name IN (
    'total_users',
    'active_users_daily',
    'total_trades',
    'total_volume_usd',
    'revenue_usd',
    'new_signups',
    'churn_rate'
  )
  AND period_start = DATE_TRUNC('day', NOW())
ORDER BY metric_name;

-- Grants
GRANT SELECT, INSERT, UPDATE ON admin.platform_analytics TO trading_app;
GRANT SELECT ON admin.platform_analytics TO trading_readonly;
GRANT USAGE, SELECT ON SEQUENCE admin.platform_analytics_id_seq TO trading_app;
GRANT SELECT ON admin.v_recent_metrics TO trading_app;
GRANT SELECT ON admin.v_platform_kpis TO trading_app;
GRANT EXECUTE ON FUNCTION admin.upsert_metric TO trading_app;
