-- ============================================================================
-- PROCEDURE: sincronizar_realizado_de_integracao
-- ============================================================================
-- Processa até 10 milhões de linhas com máxima performance
-- Tudo ocorre no banco de dados com zero transferência de dados para PHP
-- ============================================================================

CREATE OR REPLACE PROCEDURE back.sincronizar_realizado_de_integracao(
    p_cdintegracao INTEGER,
    p_sql_origem TEXT,
    OUT p_total_inseridos INTEGER,
    OUT p_total_ignorados INTEGER,
    OUT p_total_erros INTEGER,
    OUT p_mensagem TEXT,
    p_anoref INTEGER DEFAULT EXTRACT(YEAR FROM NOW())::INTEGER,
    p_mesref INTEGER DEFAULT EXTRACT(MONTH FROM NOW())::INTEGER
)
LANGUAGE plpgsql
AS $$
DECLARE
    v_idlote UUID;
    v_dtcriacao TIMESTAMP;
    v_batch_size INTEGER := 10000;
    v_processados INTEGER := 0;
    v_max_registros INTEGER := 10000000;  -- 10 milhões
    v_temp_table TEXT := 'temp_integracao_' || p_cdintegracao || '_' || (EXTRACT(EPOCH FROM NOW())::BIGINT);
BEGIN
    -- Inicializar contadores
    p_total_inseridos := 0;
    p_total_ignorados := 0;
    p_total_erros := 0;
    v_dtcriacao := NOW();
    v_idlote := gen_random_uuid();
    
    -- ========================================================================
    -- VALIDAÇÕES INICIAIS
    -- ========================================================================
    IF p_cdintegracao IS NULL OR p_cdintegracao <= 0 THEN
        p_mensagem := 'cdintegracao inválido';
        RETURN;
    END IF;
    
    IF p_sql_origem IS NULL OR TRIM(p_sql_origem) = '' THEN
        p_mensagem := 'sql_origem vazio';
        RETURN;
    END IF;
    
    IF p_anoref < 2000 OR p_anoref > 2100 THEN
        p_mensagem := 'anoref fora do intervalo válido';
        RETURN;
    END IF;
    
    IF p_mesref < 1 OR p_mesref > 12 THEN
        p_mensagem := 'mesref fora do intervalo 1-12';
        RETURN;
    END IF;
    
    -- Validar que SQL é SELECT (segurança)
    IF NOT (UPPER(TRIM(p_sql_origem)) ~ '^\s*(SELECT|WITH)' AND 
            NOT UPPER(TRIM(p_sql_origem)) ~ '(INSERT|UPDATE|DELETE|DROP|TRUNCATE|ALTER|CREATE)') THEN
        p_mensagem := 'SQL deve ser apenas SELECT ou WITH';
        RETURN;
    END IF;
    
    -- ========================================================================
    -- CRIAR TABELA TEMPORÁRIA (UNLOGGED para máxima velocidade)
    -- ========================================================================
    EXECUTE format('
        CREATE UNLOGGED TABLE %I (
            cdintegracao_empresa INTEGER,
            cdplanocontabil INTEGER,
            tpmovimento CHAR(1),
            dtmovimento DATE,
            dscomplemento VARCHAR(500),
            valor NUMERIC(15,2),
            cdcentrocusto INTEGER
        )
    ', v_temp_table);
    
    -- Adicionar índices temporários para performance
    EXECUTE format('CREATE INDEX idx_temp_empresa ON %I (cdintegracao_empresa)', v_temp_table);
    
    BEGIN
        -- ====================================================================
        -- INSERIR DADOS DA ORIGEM NA TEMP TABLE (com LIMIT de segurança)
        -- ====================================================================
        EXECUTE format('
            INSERT INTO %I (cdintegracao_empresa, cdplanocontabil, tpmovimento, dtmovimento, dscomplemento, valor, cdcentrocusto)
            WITH raw_data AS (
                %s
                LIMIT %L
            )
            SELECT
                (raw_data.cdempresa)::INTEGER,
                (raw_data.cdplanocontabil)::INTEGER,
                UPPER(TRIM(raw_data.tpmovimento))::CHAR(1),
                (raw_data.dtmovimento)::DATE,
                SUBSTRING(COALESCE(raw_data.dscomplemento, ''), 1, 500),
                (raw_data.valor)::NUMERIC(15,2),
                (raw_data.cdcentrocusto)::INTEGER
            FROM raw_data
        ', v_temp_table, p_sql_origem, v_max_registros);
        
        GET DIAGNOSTICS v_processados = ROW_COUNT;
        RAISE NOTICE '[SYNC %] Registros extraídos da origem: %', p_cdintegracao, v_processados;
        
    EXCEPTION WHEN OTHERS THEN
        p_mensagem := 'Erro ao extrair dados da origem: ' || SQLERRM;
        EXECUTE format('DROP TABLE IF EXISTS %I', v_temp_table);
        RETURN;
    END;
    
    IF v_processados = 0 THEN
        p_mensagem := 'Nenhum registro na origem';
        EXECUTE format('DROP TABLE IF EXISTS %I', v_temp_table);
        RETURN;
    END IF;
    
    -- ====================================================================
    -- INSERIR EM back.realizado COM JOINS E VALIDAÇÕES
    -- ====================================================================
    BEGIN
        EXECUTE format('
            INSERT INTO back.realizado (
                cdempresa, cdexercicio, cdcentrocusto, cdplanocontabil,
                mesref, anoref, tpmovimento, valor, dscomplemento, dtmovimento,
                cdintegracao, florigem, json_origem, flcancelado, dtcriacao, cdpessoa, idlote
            )
            SELECT DISTINCT ON (t.cdintegracao_empresa, t.cdplanocontabil, t.tpmovimento, t.dtmovimento)
                e.cdempresa,
                ex.cdexercicio,
                COALESCE(t.cdcentrocusto, 0),
                t.cdplanocontabil,
                %L,
                %L,
                t.tpmovimento,
                t.valor,
                t.dscomplemento,
                t.dtmovimento,
                %L,
                2::SMALLINT,
                json_build_object(
                    ''cdintegracao_empresa'', t.cdintegracao_empresa,
                    ''cdplanocontabil'', t.cdplanocontabil,
                    ''tpmovimento'', t.tpmovimento,
                    ''dtmovimento'', t.dtmovimento,
                    ''valor'', t.valor
                ),
                FALSE::BOOLEAN,
                %L,
                0::INTEGER,
                %L
            FROM %I t
            INNER JOIN back.empresa e ON e.cdintegracao = t.cdintegracao_empresa
            INNER JOIN back.exercicio ex ON ex.cdempresa = e.cdempresa AND ex.anoref = %L AND ex.flativo = FALSE
            WHERE t.cdintegracao_empresa IS NOT NULL
              AND e.cdempresa IS NOT NULL
              AND ex.cdexercicio IS NOT NULL
              AND t.cdplanocontabil IS NOT NULL
              AND t.tpmovimento IN (''D'', ''C'')
              AND t.dtmovimento >= ''2000-01-01''::DATE
              AND t.valor IS NOT NULL
        ', p_mesref, p_anoref, p_cdintegracao, v_dtcriacao, v_idlote, v_temp_table, p_anoref);
        
        GET DIAGNOSTICS p_total_inseridos = ROW_COUNT;
        RAISE NOTICE '[SYNC %] Registros inseridos: %', p_cdintegracao, p_total_inseridos;
        
    EXCEPTION WHEN OTHERS THEN
        p_mensagem := 'Erro ao inserir em back.realizado: ' || SQLERRM;
        RAISE NOTICE 'ERRO: %', p_mensagem;
    END;
    
    -- ====================================================================
    -- CALCULAR IGNORADOS E ERROS
    -- ====================================================================
    p_total_ignorados := v_processados - p_total_inseridos;
    p_total_erros := 0;
    
    -- ====================================================================
    -- ATUALIZAR TIMESTAMP DA INTEGRAÇÃO
    -- ====================================================================
    BEGIN
        UPDATE back.integracao
        SET ultima_sincronizacao = NOW()
        WHERE cdintegracao = p_cdintegracao;
    EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'Aviso ao atualizar timestamp: %', SQLERRM;
    END;
    
    -- ====================================================================
    -- LIMPAR RECURSOS
    -- ====================================================================
    EXECUTE format('DROP TABLE IF EXISTS %I', v_temp_table);
    
    -- ====================================================================
    -- MENSAGEM FINAL
    -- ====================================================================
    p_mensagem := format(
        'Integração %s: %s inseridos, %s ignorados, %s erros. Lote: %s',
        p_cdintegracao,
        p_total_inseridos,
        p_total_ignorados,
        p_total_erros,
        v_idlote
    );
    
    RAISE NOTICE '%', p_mensagem;
    
EXCEPTION WHEN OTHERS THEN
    p_mensagem := 'Erro fatal na sincronização: ' || SQLERRM;
    RAISE NOTICE 'ERRO FATAL: %', p_mensagem;
    EXECUTE format('DROP TABLE IF EXISTS %I', v_temp_table);
END;
$$;

-- ============================================================================
-- COMENTÁRIOS E ÍNDICES DE SUPORTE
-- ============================================================================
COMMENT ON PROCEDURE back.sincronizar_realizado_de_integracao IS 
'Sincroniza dados de integração externas para back.realizado.
 Suporta até 10 milhões de linhas com máxima performance.
 Tudo processa no banco de dados.
 Uso: CALL back.sincronizar_realizado_de_integracao(16, sql_text, p_total_inseridos, p_total_ignorados, p_total_erros, p_mensagem)';
