-- ============================================================================
-- 12-analytics-kpis-ddl.sql
-- Schema: reports (extension)
-- ERP Construccion - KPIs Configurables (GAP-001)
-- ============================================================================
-- Descripcion: Configuracion dinamica de KPIs incluyendo:
--   - Definicion de KPIs con formulas configurables
--   - Valores calculados historicos
--   - Umbrales y semaforizacion
-- ============================================================================
-- Autor: Claude-Especialista-BD
-- Fecha: 2026-02-04
-- Version: 1.0.0
-- Tarea: TASK-2026-02-03-ANALISIS-MODELADO-INTEGRAL / GAP-001
-- ============================================================================

-- Usar schema reports existente
-- CREATE SCHEMA IF NOT EXISTS reports;

-- ============================================================================
-- TABLAS
-- ============================================================================

-- ----------------------------------------------------------------------------
-- 1. Configuracion de KPIs
-- Permite definir KPIs dinamicos con formulas configurables
-- ----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS reports.kpis_config (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    -- Identificacion
    code VARCHAR(50) NOT NULL,
    name VARCHAR(200) NOT NULL,
    description TEXT,

    -- Clasificacion
    category VARCHAR(50) NOT NULL, -- financial, progress, quality, hse, hr, inventory, operational
    module VARCHAR(50) NOT NULL,   -- MAI-006, MAE-014, etc.

    -- Formula de calculo
    formula TEXT NOT NULL,         -- SQL o expresion matematica
    formula_type VARCHAR(20) NOT NULL DEFAULT 'sql', -- sql, expression, function
    query_function VARCHAR(255),   -- Nombre de funcion PL/pgSQL si aplica

    -- Parametros de la formula
    parameters_schema JSONB DEFAULT '{}',

    -- Unidad y formato
    unit VARCHAR(20),              -- %, $, hrs, dias, etc.
    decimal_places INTEGER DEFAULT 2,
    format_pattern VARCHAR(50),    -- Patron de formato para display

    -- Umbrales de semaforizacion
    target_value DECIMAL(18,4),
    threshold_green DECIMAL(18,4), -- Valor >= este es verde
    threshold_yellow DECIMAL(18,4), -- Valor >= este es amarillo, < es rojo
    invert_colors BOOLEAN DEFAULT FALSE, -- TRUE si menor es mejor

    -- Frecuencia de calculo
    calculation_frequency VARCHAR(20) DEFAULT 'daily', -- realtime, hourly, daily, weekly, monthly

    -- Visualizacion
    display_order INTEGER DEFAULT 0,
    icon VARCHAR(50),
    color VARCHAR(20),

    -- Estado
    is_active BOOLEAN NOT NULL DEFAULT TRUE,
    is_system BOOLEAN NOT NULL DEFAULT FALSE, -- TRUE = no editable por usuario

    -- Metadatos
    metadata JSONB,

    -- Auditoria
    created_by UUID,
    updated_by UUID,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    deleted_at TIMESTAMPTZ,

    CONSTRAINT uq_kpis_config_tenant_code UNIQUE (tenant_id, code)
);

-- ----------------------------------------------------------------------------
-- 2. Valores Calculados de KPIs
-- Almacena los valores calculados periodicamente
-- ----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS reports.kpis_values (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL,

    -- KPI
    kpi_id UUID NOT NULL REFERENCES reports.kpis_config(id) ON DELETE CASCADE,

    -- Periodo
    period_start DATE NOT NULL,
    period_end DATE NOT NULL,
    period_type VARCHAR(20) NOT NULL DEFAULT 'daily', -- daily, weekly, monthly, quarterly, yearly

    -- Contexto opcional
    project_id UUID,           -- Fraccionamiento/Obra especifica
    department_id UUID,        -- Departamento especifico

    -- Valor calculado
    value DECIMAL(18,4) NOT NULL,
    previous_value DECIMAL(18,4),

    -- Comparacion con objetivo
    target_value DECIMAL(18,4),
    variance_value DECIMAL(18,4),     -- value - target_value
    variance_percentage DECIMAL(8,2), -- ((value - target) / target) * 100

    -- Semaforizacion calculada
    status VARCHAR(10), -- green, yellow, red
    is_on_target BOOLEAN,

    -- Tendencia
    trend_direction VARCHAR(10), -- up, down, stable
    change_percentage DECIMAL(8,2),

    -- Desglose
    breakdown JSONB, -- Datos adicionales de calculo

    -- Calculo
    calculated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    calculation_duration_ms INTEGER,
    calculation_error TEXT,

    -- Auditoria
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- ============================================================================
-- INDICES
-- ============================================================================

-- KPIs Config
CREATE INDEX IF NOT EXISTS idx_kpis_config_tenant ON reports.kpis_config(tenant_id);
CREATE INDEX IF NOT EXISTS idx_kpis_config_tenant_category ON reports.kpis_config(tenant_id, category);
CREATE INDEX IF NOT EXISTS idx_kpis_config_tenant_module ON reports.kpis_config(tenant_id, module);
CREATE INDEX IF NOT EXISTS idx_kpis_config_active ON reports.kpis_config(tenant_id, is_active) WHERE is_active = TRUE;

-- KPIs Values
CREATE INDEX IF NOT EXISTS idx_kpis_values_tenant ON reports.kpis_values(tenant_id);
CREATE INDEX IF NOT EXISTS idx_kpis_values_kpi ON reports.kpis_values(kpi_id);
CREATE INDEX IF NOT EXISTS idx_kpis_values_period ON reports.kpis_values(tenant_id, period_start, period_end);
CREATE INDEX IF NOT EXISTS idx_kpis_values_kpi_period ON reports.kpis_values(kpi_id, period_start DESC);
CREATE INDEX IF NOT EXISTS idx_kpis_values_project ON reports.kpis_values(tenant_id, project_id) WHERE project_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_kpis_values_calculated ON reports.kpis_values(calculated_at DESC);

-- ============================================================================
-- ROW LEVEL SECURITY (RLS)
-- ============================================================================

ALTER TABLE reports.kpis_config ENABLE ROW LEVEL SECURITY;
ALTER TABLE reports.kpis_values ENABLE ROW LEVEL SECURITY;

-- ============================================================================
-- TRIGGERS DE AUDITORIA
-- ============================================================================

CREATE OR REPLACE FUNCTION reports.set_kpis_updated_at()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS trg_kpis_config_updated_at ON reports.kpis_config;
CREATE TRIGGER trg_kpis_config_updated_at
    BEFORE UPDATE ON reports.kpis_config
    FOR EACH ROW EXECUTE FUNCTION reports.set_kpis_updated_at();

-- ============================================================================
-- FUNCIONES AUXILIARES
-- ============================================================================

-- Funcion para calcular un KPI especifico
CREATE OR REPLACE FUNCTION reports.calculate_kpi(
    p_kpi_id UUID,
    p_tenant_id UUID,
    p_period_start DATE,
    p_period_end DATE,
    p_project_id UUID DEFAULT NULL
)
RETURNS DECIMAL(18,4) AS $$
DECLARE
    v_kpi RECORD;
    v_result DECIMAL(18,4);
BEGIN
    -- Obtener configuracion del KPI
    SELECT * INTO v_kpi
    FROM reports.kpis_config
    WHERE id = p_kpi_id AND tenant_id = p_tenant_id AND is_active = TRUE;

    IF NOT FOUND THEN
        RAISE EXCEPTION 'KPI not found or inactive: %', p_kpi_id;
    END IF;

    -- Si es una funcion, ejecutarla
    IF v_kpi.formula_type = 'function' AND v_kpi.query_function IS NOT NULL THEN
        EXECUTE format('SELECT %s($1, $2, $3, $4)', v_kpi.query_function)
        INTO v_result
        USING p_tenant_id, p_period_start, p_period_end, p_project_id;
    ELSE
        -- Ejecutar formula SQL directamente (con cuidado de seguridad)
        -- En produccion esto debe ser mas restrictivo
        EXECUTE v_kpi.formula
        INTO v_result
        USING p_tenant_id, p_period_start, p_period_end, p_project_id;
    END IF;

    RETURN v_result;
EXCEPTION
    WHEN OTHERS THEN
        RAISE WARNING 'Error calculating KPI %: %', p_kpi_id, SQLERRM;
        RETURN NULL;
END;
$$ LANGUAGE plpgsql;

-- Funcion para determinar color de semaforo
CREATE OR REPLACE FUNCTION reports.get_kpi_status(
    p_value DECIMAL(18,4),
    p_threshold_green DECIMAL(18,4),
    p_threshold_yellow DECIMAL(18,4),
    p_invert_colors BOOLEAN DEFAULT FALSE
)
RETURNS VARCHAR(10) AS $$
BEGIN
    IF p_threshold_green IS NULL OR p_threshold_yellow IS NULL THEN
        RETURN NULL;
    END IF;

    IF p_invert_colors THEN
        -- Menor es mejor
        IF p_value <= p_threshold_green THEN
            RETURN 'green';
        ELSIF p_value <= p_threshold_yellow THEN
            RETURN 'yellow';
        ELSE
            RETURN 'red';
        END IF;
    ELSE
        -- Mayor es mejor
        IF p_value >= p_threshold_green THEN
            RETURN 'green';
        ELSIF p_value >= p_threshold_yellow THEN
            RETURN 'yellow';
        ELSE
            RETURN 'red';
        END IF;
    END IF;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

-- ============================================================================
-- COMENTARIOS DE DOCUMENTACION
-- ============================================================================

COMMENT ON TABLE reports.kpis_config IS 'Configuracion de KPIs dinamicos con formulas (GAP-001)';
COMMENT ON COLUMN reports.kpis_config.formula IS 'Formula SQL o expresion para calcular el KPI';
COMMENT ON COLUMN reports.kpis_config.threshold_green IS 'Umbral para status verde';
COMMENT ON COLUMN reports.kpis_config.threshold_yellow IS 'Umbral para status amarillo';
COMMENT ON COLUMN reports.kpis_config.invert_colors IS 'TRUE si valores menores son mejores';

COMMENT ON TABLE reports.kpis_values IS 'Valores calculados historicos de KPIs (GAP-001)';
COMMENT ON COLUMN reports.kpis_values.variance_value IS 'Diferencia absoluta: value - target';
COMMENT ON COLUMN reports.kpis_values.variance_percentage IS 'Diferencia porcentual respecto al target';

-- ============================================================================
-- FIN DEL SCRIPT
-- ============================================================================
