/*
Você atuará como especialista para implementar novos métodos e fazer correções.

Contexto técnico:
    a) Linguagem de desenvolvimento PHP 7.4
    b) Framework MadBuilder fork do Adianti Framework
    c) Banco de dados MySQL
    d) Sistema com estrutura para matriz e múltiplas filiais

Diretrizes obrigatórias:
    Restrição crítica:
        Não há acesso ao servidor
        A refatoração é permitida dentro do bloco onde começa:
        //Inicio do bloco que pode refatorar ---------------------------------------------------------------------

        //Fim do bloco que pode refatorar ------------------------------------------------------------------------
 
        Existem metodos ou trechos comentando isso quer dizer que não sõ utilizados e devem ser ignorados.
        Antes de perguntar se algum metodo ou função existe leia o arquivo enviado.
        
    Resposta:
        Direta
        Técnica
        Sem introduções
        Sem tutoriais
        Sem pseudocódigo
        Sem trechos incompletos
        A correção ou implementação do metodo deve ser gerada de forma completa pronto para copiar e colar

    Proibido alterar:
        Nomes de variáveis
        Métodos
        Classes
        Campos
        IDs
        Eventos
        Fluxo principal existente
        Mantenha integralmente a arquitetura existente para manter a compatibilidade do projeto existente.

    Entrega:
        A correção ou implementação do metodo deve ser gerada de forma completa pronto para copiar e colar
        Sempre destacar dentro do metodo o que foi implementado e para que finalidade e ainda colocar a data e hora e quem foi que gerou
        Sempre me responda em português do Brasil, utilizando termos técnicos nativos da área de desenvolvimento e programação quando necessário. Nunca mude para o inglês, a menos que eu peça explicitamente.

    Debug:
        Se for solicitado para gerar um DEBUG deve usar TScript::create("console.log('=== DEBUG onOpenCalendarForm FINALIZADO ===');");
        O debug deve ser iniciado na primeira linha do metodo ate a ultima linha afim de pegar todo o processo e colocar a seguinte observação exemplo debug-onsave 01 /  debug-onsave 02 a medida que for incluindo ir colocando a sequencia
        Não altere nada do codigo oiginal simplismente coloque o debug para encontrar o erro.
        So pode retirar o debug depois que for solicitado por escrito.

    Prioridade:
        Soluções compatíveis com a versão do framework
        NAO CRIE NOVOS METODOS, CAMPOS, FUNÇÕES PERGUNTE ANTES DE PROSSEGUIR

    Contexto insuficiente:
        Perguntar antes de prosseguir

    Objetivo:
        Corrigir
        Implementar
        E preservar a compatibilidade total com as regras existentes
        
    Instrução final:
        Analise o arquivo enviado
        Não forneça nenhuma solução fora das regras citadas acima.
        Se perceber que a solução proposta não esta funcionando, sugira colocar um debug na terceira tentativa.
        Se o arquivo enviado for considerado grande e para evitar o reinicio a todo momento da geração, divada o processo em partes e vai entregando os resultados
        Gere um arquivo com instruções do que foi gerado passo a passo e sendo necessario recomeçar possa iniciar de onde parou, para nao ficar perdendo tempo regerando a mesma solução varias vezes

    Tarefa:

//--------------------------------------------------------------------------------------------------------

Usa BootstrapFormBuilder, TDataGrid, TPageNavigation, com appendPage(), addFields() com layout em array
Métodos: onShow (para abrir form), onEdit, onSave, onDelete, onSearch, onReload
Banco definido em variável estática private static $database = 'bdgestorweb', self::$database, self::$activeRecord
Form: modal com parent::setSize(0.60, null)
Form extende TWindow (não TStandardForm)
List extende TPage (não TStandardList)
Datagrid usa BootstrapDatagridWrapper com disableDefaultClick()
Actions usam setUseButton(false) e setButtonClass()
Usa TBreadCrumb::create() em vez de TXMLBreadCrumb

//--------------------------------------------------------------------------------------------------------
Resumo da compreensão:

Papel: Especialista em implementação/correção de código PHP 7.4 no MadBuilder (fork Adianti)
Restrições críticas:

Refatorar APENAS entre blocos comentados específicos
Sem acesso ao servidor
Nenhuma alteração em nomes, métodos, classes, variáveis, IDs, eventos
Manter fluxo principal intacto


Entrega obrigatória:

Código completo e pronto para substituição imediata
Sem pseudocódigo, tutoriais ou explicações longas
Resposta direta e técnica


Debug:

Console.log ou TMessage apenas
Do início ao fim do método
Remover apenas com autorização explícita


Processo:

Analisar integralmente o arquivo ANTES de propor mudanças
Perguntar se contexto for insuficiente
Preservar 100% compatibilidade com sistema existente
//--------------------------------------------------------------------------------------------------------
Você atuará como especialista para implementar novos metodos e corrigir metodos existentes em

Contexto técnico:
    a)Ambiente PHP 7.4.
    b)Framework Madbuilder é um Fork do Adianti Framework.
    c)Bando de dados MYSQL
    d)Sistema desenvolvido utiliza recurso de matriz e varias filiais
    E)Você atuará como especialista para implementar e fazer correções

Diretrizes para resposta:
    1)Restrição crítica: Não tenho acesso ao servidor para realizar procedimentos, as modificações devem ser feitas exclusivamente nos blocos comentados onde esta escrito 
             //Inicio do bloco que pode refatorar ---------------------------------------------------------------------
            
             //Fim do bloco que pode refatorar ------------------------------------------------------------------------
    2)Objetividade: Resposta direta e sem introduções ou explicações, a menos que eu solicite e não incluir Explicações longas,Introduções,Tutoriais,Comentários desnecessários,Trechos incompletos,Pseudocódigo
    3)Fidelidade ao código: Proibido alterar nomenclaturas de variáveis, métodos ou classes originais.
    4)Entrega completa: gere exclusivamente o método completo corrigido ou novo método solicitado, pronto para substituição imediata, sem alterar nomenclaturas existentes.
    5)Debug: Se for solicitado debug , você deve fazer via console.log do navegador ou usando o TMessage do framework e ou atraves de var_dump.
    6)Priorize soluções que não exijam acesso ao servidor
    7)Evite suposições, se faltar contexto, pergunte antes de fazer qualquer modificação.
    8)Objetivo e refatorar e corrigir mantendo sempre a compatibilidade total com o código existente.
    9)Leia o arquivo enviado nele ja contem todas as informações sobre a tarefa.

Tarefa:
Refatore o código abaixo seguindo as Diretrizes para resposta.
//--------------------------------------------------------------------------------------------------------
E me forneca a solução dentro do que foi estabelecido nas regras


Dica de mestre: Se a IA começar a "alucinar" nomes de classes que não existem no Adianti, responda apenas: "Você violou a regra 2. Releia as nomenclaturas originais e corrija." Ela costuma entrar na linha na hora.
//-------------------------------------------------------------------

1)PRECISO QUE SEJA OBJETIVO EM SUA RESPOSTA, NAO FIQUE CRIANDO OU ALTERANDO NOMEMCLATURAS DO CODIGO ORIGINAL SEM PERGUNTAR ANTES
2)UTILIZO MADBUILDER PHP FORK DO ADIANTI FRAMEWORK 7.4
3)NAO TENHO ACESSO AO SERVIDOR PARA REALIZAR PROCEDIMENTOS
4)GERAÇÃO DE LOG PELO CONSOLE DO NAVEGADOR OU USANDO ALGUM RECURSO VISUAL
5)FAÇA BACKUP DO CÓDIGO ORIGINAL
6)APRESENTE A CORREÇÃO OU MELHORIA DO METODO POR COMPLETO PARA EVITAR ERROS
7)SO PODE FAZER MODIFICAÇÃO NO FRAMEWORK ONDE ESTA ESCRITO //Aqui pode incluir---------------------------------------------------------------------
REFATORAR

2. Técnica do "Readonly" Temporário (JavaScript)
Esta é a técnica mais robusta atualmente. O navegador não aplica autocomplete em campos marcados como readonly. Podemos desabilitar o readonly assim que o usuário clica no campo.

Você pode adicionar isso no onShow() da sua página ou em um arquivo JS global do sistema:

Projeto:
Criar projeto chamado SimuladorDePreco que atenda a precificação de produtos ou serviços.
Cadastrar em tabela os registros que foram gerados pela simulação, e com isso acompanhar a evolução destas simulação.
Incluir despesas sendo elas federal, estadual e municipal e tambem local como agua, luz, entre outros gastos e tudo deve ser calculado penso que a melhor forma seja em porcentagem.
Cadastrar varios produtos ou serviço para a empresa ou filial que for utilizar separadamente.
Como utiliza o conceito de empresa e filias pode ter diferença nos precos praticados entre elas.
Fazer pesquisa na internet com a fonte , data e hora e site ou o meio utilizado para encontrar os preços praticados na regiao onde a empresa esta situada e caso nao encontre procurar em cidades proximas para poder o usuario avaliar se o preço esta ou nao muito maior que o concorrente.
Sugerir melhorias para calcular o preco de venda ou servico utilizando melhores praticas do mercado e ate mesmo utilização de metodoliga cientifica
Sugerir melhorias e seguir as boas praticas de mercado para o desenvolvimento deste projeto.
Criar as tabelas, codigos e tudo mais para ser implementados no sistema.
Prefixo das tabelas deve ser simuladordepreco_xxx e por ai vai
sk-3aeee3f5ceb342b99b25245fa3573db8

https://www.scrapy.org/download
https://langsearch.com
https://www.search1api.com/
https://typesense.org/
https://webatendimento.saude.gov.br/
*/

/*
//------------------------------------------------------

/*
<span style="font-size: 9pt; font-weight: bold; color:#FFFFFF;" <b>Versão 1.0.0.0 - B4.2</b></span><br> <html> <script>     function construirArray(qtdElementos)      {         this.length = qtdElementos     }     var arrayDia = new construirArray(7);     arrayDia[0] = 'Domingo';     arrayDia[1] = 'Segunda';     arrayDia[2] = 'Terça';     arrayDia[3] = 'Quarta';     arrayDia[4] = 'Quinta';     arrayDia[5] = 'Sexta';     arrayDia[6] = 'Sabado';      var arrayMes = new construirArray(12);     arrayMes[0] = 'Janeiro';     arrayMes[1] = 'Fevereiro';     arrayMes[2] = 'Março';     arrayMes[3] = 'Abril';     arrayMes[4] = 'Maio';     arrayMes[5] = 'Junho';     arrayMes[6] = 'Julho';     arrayMes[7] = 'Agosto';     arrayMes[8] = 'Setembro';     arrayMes[9] = 'Outubro';     arrayMes[10] = 'Novembro';     arrayMes[11] = 'Dezembro';      function mostrarDataHora(hora, diaSemana, dia, mes, ano)      {         retorno = '' + hora + '<br> ';         retorno += '' + diaSemana + ' ' + dia + ' de ' + mes + ' de ' + ano;         document.getElementById('datahora').innerHTML = retorno;     }      function getMesExtenso(mes)      {         return this.arrayMes[mes];     }      function getDiaExtenso(dia)      {         return this.arrayDia[dia];     }      function atualizarDataHora()      {         dataAtual = new Date();         dia = dataAtual.getDate();         diaSemana = getDiaExtenso(dataAtual.getDay());         mes = getMesExtenso(dataAtual.getMonth());         ano = dataAtual.getFullYear();         hora = dataAtual.getHours();         minuto = dataAtual.getMinutes();         segundo = dataAtual.getSeconds();         horaImprimivel = hora + ':' + minuto + ':' + segundo;         mostrarDataHora(horaImprimivel, diaSemana, dia, mes, ano);         setTimeout('atualizarDataHora()', 1000);     } </script>  <body onload='atualizarDataHora()'>     <font color='#FFFFFF'> <b>             <div id='datahora'></div>     </font> </b> </body>  </html>
<span style="font-size: 9pt; color: #FFFFFF;">{userunitname}</span>
*/
/*
//----------------------------------------------------

    <div style="padding: 5px; border-left: 5px solid #414749; margin-bottom: 15px;">
        <strong style="color: #414749; font-weight: bold;">{$username}</strong> 
        <strong span style="color: #414749;"><br> SEJA BEM-VINDO(A) AO SEU AMBIENTE DE TRABALHO</strong></span>
    </div>

    <div style="padding: 5px; border-left: 5px solid #414749; margin-bottom: 15px;">
        <strong style="color: #414749; font-weight: bold;">VOCÊ ESTÁ EM AMBIENTE DE</strong> 
        <span style="color: #414749; font-weight: bold;">{$descricao_bdtipo}</span>
    </div>

*/
/*
STATUS: LINEARIZADO (Tratamento de erros HTTP sem loops de MutationObserver)

let timeoutFaxina = null;

function executarFaxina() 
{
    if (timeoutFaxina) clearTimeout(timeoutFaxina);
    timeoutFaxina = setTimeout(() => 
    {
        const modais = document.querySelectorAll('.modal, .ui-dialog');
        modais.forEach(modal => 
        {
            if (modal.textContent.includes('503') || modal.textContent.includes('Requisição falhou')) 
            {
                modal.style.display = 'none';
                document.body.classList.remove('modal-open');
                document.querySelectorAll('.modal-backdrop').forEach(b => b.remove());
                document.body.style.overflow = '';
                document.body.style.pointerEvents = '';
            }
        });
    }, 300);
}

// Intercepta e loga no console apenas falhas reais de resposta do servidor (ex: 500 ou 503)
$(document).ajaxError((event, jqxhr, settings) => 
{
    if (jqxhr.status === 401) return;
    if (jqxhr.status === 503 || jqxhr.status === 500) 
    {
        console.error("[MonitorConexao] HTTP " + jqxhr.status + " na URL: " + (settings.url || "Desconhecida"));
        executarFaxina();
    }
});
*/
/*
<?php
require_once 'init.php';
$theme  = $ini['general']['theme'];
$class  = isset($_REQUEST['class']) ? $_REQUEST['class'] : '';
$public = in_array($class, $ini['permission']['public_classes']);

// AdiantiCoreApplication::setRouter(array('AdiantiRouteTranslator', 'translate'));

new TSession;
ApplicationTranslator::setLanguage( TSession::getValue('user_language'), true );
BuilderTranslator::setLanguage( TSession::getValue('user_language'), true );

MadLogService::initializeDebugLogging();

$content = BuilderTemplateParser::init('layout');
$content = ApplicationTranslator::translateTemplate($content);

// Define uma versão baseada na data da última alteração do arquivo principal ou uma constante
$versao_cache = date('YmdHis', filemtime('app/lib/include/application.js')); // Ajuste para um arquivo que você sempre altera
// Regex para encontrar arquivos .js e .css e adicionar a query string
$content = preg_replace('/(href|src)=["\']([^"\']+\.(js|css))["\']/i', '$1="$2?v=' . $versao_cache . '"', $content);

echo $content;

if (TSession::getValue('logged') OR $public)
{
    if ($class)
    {
        $method = isset($_REQUEST['method']) ? $_REQUEST['method'] : NULL;
        AdiantiCoreApplication::loadPage($class, $method, $_REQUEST);
    }
}
else
{
    if (isset($ini['general']['public_view']) && $ini['general']['public_view'] == '1')
    {
        if (!empty($ini['general']['public_entry']))
        {
            AdiantiCoreApplication::loadPage($ini['general']['public_entry'], '', $_REQUEST);
        }
    }
    else
    {
        AdiantiCoreApplication::loadPage('LoginForm', '', $_REQUEST);
    }
}

// Substitui a meta tag depreciada
$content = str_replace(
    '<meta name="apple-mobile-web-app-capable" content="yes">',
    '<meta name="mobile-web-app-capable" content="yes">',
    $content
);

// Injeção do monitoramento (se aplicável)
$isAjax = (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
if (!$isAjax && TSession::getValue('logged') && class_exists('AgendamentoNotificacaoGlobal')) 
{
    AgendamentoNotificacaoGlobal::inject();
}

MadLogService::finalizeDebugLogging();

*/

/*
//----------------------------------------------------------------------
//Desabilitar autocomplete
(function() 
{
    function disableAutocompleteForField(field) 
    {
        if (!field || !field.parentNode || field.dataset.processed) return;

        const clone = field.cloneNode(true);

        clone.setAttribute('autocomplete', 'off');
        clone.removeAttribute('id');
        clone.removeAttribute('name');

        // Marca o campo como processado
        field.dataset.processed = 'true';

        // Substitui o campo original após pequeno delay
        setTimeout(function() 
        {
            field.parentNode.replaceChild(clone, field);
        }, 100);
    }

    function processForm(form) 
    {
        if (!form || form.dataset.autocompleteDisabled) return;

        form.setAttribute('autocomplete', 'off');
        form.dataset.autocompleteDisabled = 'true';

        form.querySelectorAll('input[type=text], input[type=password], input[type=email], input[type=tel], textarea')
            .forEach(disableAutocompleteForField);
    }

    function initAntiAutocomplete() 
    {
        document.querySelectorAll('form').forEach(processForm);
    }

    // Executa ao carregar o DOM
    document.addEventListener('DOMContentLoaded', initAntiAutocomplete);

    // Observa mutações no DOM para suportar Adianti (AJAX)
    const observer = new MutationObserver(function(mutations) 
    {
        mutations.forEach(function(mutation) 
        {
            mutation.addedNodes.forEach(function(node) 
            {
                if (node.nodeType === 1) 
                {
                    if (node.tagName === 'FORM') 
                    {
                        processForm(node);
                    } 
                    else 
                    {
                        node.querySelectorAll?.('form').forEach(processForm);
                    }
                }
            });
        });
    });

    observer.observe(document.body, { childList: true, subtree: true });
})();

//--------------------------------------------

// Arquivo: index.php

require_once 'init.php';
$theme  = $ini['general']['theme'];
$class  = isset($_REQUEST['class']) ? $_REQUEST['class'] : '';
$public = in_array($class, $ini['permission']['public_classes']);

// AdiantiCoreApplication::setRouter(array('AdiantiRouteTranslator', 'translate'));

new TSession;
ApplicationTranslator::setLanguage( TSession::getValue('user_language'), true );
BuilderTranslator::setLanguage( TSession::getValue('user_language'), true );

MadLogService::initializeDebugLogging();

$content = BuilderTemplateParser::init('layout');
$content = ApplicationTranslator::translateTemplate($content);

// Define uma versão baseada na data da última alteração do arquivo principal ou uma constante
$versao_cache = date('YmdHis', filemtime('app/lib/include/application.js')); // Ajuste para um arquivo que você sempre altera
// Regex para encontrar arquivos .js e .css e adicionar a query string
$content = preg_replace('/(href|src)=["\']([^"\']+\.(js|css))["\']/i', '$1="$2?v=' . $versao_cache . '"', $content);

echo $content;

if (TSession::getValue('logged') OR $public)
{
    if ($class)
    {
        $method = isset($_REQUEST['method']) ? $_REQUEST['method'] : NULL;
        AdiantiCoreApplication::loadPage($class, $method, $_REQUEST);
    }
}
else
{
    if (isset($ini['general']['public_view']) && $ini['general']['public_view'] == '1')
    {
        if (!empty($ini['general']['public_entry']))
        {
            AdiantiCoreApplication::loadPage($ini['general']['public_entry'], '', $_REQUEST);
        }
    }
    else
    {
        AdiantiCoreApplication::loadPage('LoginForm', '', $_REQUEST);
    }
}

// Substitui a meta tag depreciada
$content = str_replace(
    '<meta name="apple-mobile-web-app-capable" content="yes">',
    '<meta name="mobile-web-app-capable" content="yes">',
    $content
);

// Injeção do monitoramento (se aplicável)
$isAjax = (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
if (!$isAjax && TSession::getValue('logged') && class_exists('AgendamentoNotificacaoGlobal')) 
{
    AgendamentoNotificacaoGlobal::inject();
}

MadLogService::finalizeDebugLogging();

//--------------------------------------------
*/

    private function bkp_gerarSlotsAPartirDeHorarios($horariosDisponiveis, $dataInicial, $dataFinal, $agendamentosExistentes = [])
    {
        $slots = [];
        try
        {

            $agendamentosMap = [];              // Finalidade: Agendamentos com cancelado = 'S' nao devem bloquear o slot do horario.
            $canceladosMap   = [];             // CORREÇÃO: 2026-06-08 - Mapa de cancelados por chave de horario para marcar o slot como vago_pos_cancelamento
            foreach ($agendamentosExistentes as $agendamento)
            {
                if (isset($agendamento->cancelado) && $agendamento->cancelado === 'S')
                {
                    // Registra que existia um cancelado neste horario/profissional para sinalizar o slot gerado
                    $keyCancelado = $agendamento->profissional_id . '|' . $agendamento->horario_inicial;
                    // [INICIO CORRECAO] 2026-06-12 - Gerado por Claude (Anthropic) Finalidade: Armazenar o ID do agendamento cancelado (origem do horario vago) em vez de apenas true.
                    $canceladosMap[$keyCancelado] = $agendamento->id;
                    // [FIM CORRECAO] 2026-06-12
                    continue;
                }
                $key = $agendamento->profissional_id . '|' . $agendamento->horario_inicial;
                $agendamentosMap[$key] = true;
            }
            // [FIM CORRECAO] 2026-06-05 10:00

            $profissionaisIds = array_unique(array_column($horariosDisponiveis, 'profissional_id'));
            $profMap = [];
            if (!empty($profissionaisIds))
            {
                $repProf  = new TRepository('SystemUsers');
                $critProf = new TCriteria();
                $critProf->add(new TFilter('id', 'IN', $profissionaisIds));
                $profs = $repProf->load($critProf);
                foreach ($profs as $p)
                {
                    $profMap[$p->id] = $p;
                }
            }

            $horariosIds         = array_unique(array_column($horariosDisponiveis, 'id'));
            $pacientesPorHorario = [];
            if (!empty($horariosIds))
            {
                $repPessoa = new TRepository('Pessoa');
                $critPac   = new TCriteria();
                $critPac->add(new TFilter('horarioatendimento_id', 'IN', $horariosIds));
                $pacs = $repPessoa->load($critPac);
                foreach ($pacs as $p)
                {
                    $pacientesPorHorario[$p->horarioatendimento_id][] = $p->id;
                }
            }

            $ambientesIds = [];
            foreach ($horariosDisponiveis as $h)
            {
                if (!empty($h->ambiente_id)) $ambientesIds[$h->ambiente_id] = true;
            }
            $ambientesIds = array_keys($ambientesIds);

            $ambMap = [];
            if (!empty($ambientesIds))
            {
                $repAmb  = new TRepository('Ambiente');
                $critAmb = new TCriteria();
                $critAmb->add(new TFilter('id', 'IN', $ambientesIds));
                $ambs = $repAmb->load($critAmb);
                foreach ($ambs as $a)
                {
                    $ambMap[$a->id] = $a;
                }
            }

            $inicioPeriodo = new DateTime(substr($dataInicial, 0, 10));
            $fimPeriodo    = new DateTime(substr($dataFinal, 0, 10));
            $fimPeriodo->modify('+1 day');

            $intervaloDias    = $inicioPeriodo->diff($fimPeriodo)->days;
            $limiteMaximoDias = 90;
            if ($intervaloDias > $limiteMaximoDias)
            {
                throw new Exception("Periodo maximo para visualizacao de horarios eh de {$limiteMaximoDias} dias. Ajuste as datas de pesquisa.");
            }

            $currentDate = clone $inicioPeriodo;

            while ($currentDate < $fimPeriodo)
            {
                $diaSemanaAtual = (int)$currentDate->format('w');
                $dataFormatada  = $currentDate->format('Y-m-d');

                foreach ($horariosDisponiveis as $horario)
                {
                    if ((int)$horario->dia_da_semana !== $diaSemanaAtual) continue;
                    if (!in_array($horario->status, ['A', 'B', 'D'])) continue;

                    $horaInicio = substr($horario->hora_inicio, 0, 5) . ':00';
                    $horaFim    = substr($horario->hora_fim, 0, 5) . ':00';
                    $slotInicio = $dataFormatada . ' ' . $horaInicio;
                    $slotFim    = $dataFormatada . ' ' . $horaFim;

                    if (isset($agendamentosMap[$horario->profissional_id . '|' . $slotInicio])) continue;

                    $slot           = new Agendamento();
                    $slotIdBase     = $slotInicio . '|' . $slotFim . '|' . $horario->profissional_id . '|' . ($horario->ambiente_id ?? '0');
                    $slot->id       = 'SLOT_' . md5($slotIdBase);

                    if ($horario->status === 'B')
                    {
                        $slot->tipo_slot       = 'BLOQUEADO';
                        $slot->status          = '99';
                        $slot->motivo_bloqueio = $horario->motivo_bloqueio ?? 'O motivo deste bloqueio nao foi informado';
                    }
                    else
                    {
                        $slot->tipo_slot = 'DISPONIVEL';
                        $slot->status    = 'LIVRE';
                    }

                    $slot->horario_inicial   = $slotInicio;
                    $slot->horario_final     = $slotFim;
                    $slot->profissional_id   = $horario->profissional_id;
                    $slot->empresa_id        = $horario->empresa_id;
                    $slot->ambiente_id       = $horario->ambiente_id ?? null;
                    $slot->cancelado         = 'N';
                    $slot->consulta_retorno  = 'N';
                    $slot->entrega_resultado = 'NE';
                    $slot->ordematendimento  = 0;
                    $slot->horario_status    = $horario->status;

                    if (isset($profMap[$horario->profissional_id]))
                    {
                        $slot->profissional = $profMap[$horario->profissional_id];
                    }

                    if (!empty($horario->ambiente_id) && isset($ambMap[$horario->ambiente_id]))
                    {
                        $slot->ambiente = $ambMap[$horario->ambiente_id];
                    }

                    // Gerado por: Claude Sonnet 4.6 | Data: 2026-06-05
                    // Correção: Quando um horario possui multiplos pacientes vinculados, gera um slot DISPONIVEL por paciente em  linha separada na grid, todos com cor vermelha via propriedade multiplos_pacientes = true. 
                    // Quando ha paciente unico, comportamento original mantido.
                    $pacientes_deste_horario = $pacientesPorHorario[$horario->id] ?? [];
                    $qtd_pacientes_horario   = count($pacientes_deste_horario);

                    // CORREÇÃO: 2026-06-08 - Gerado por Claude (Anthropic)
                    // Finalidade: Marcar o slot como "vago pos cancelamento" quando havia um agendamento  cancelado neste mesmo horario/profissional. Isso permite que a grid exiba  o slot na cor laranja com o texto "HORARIO VAGO" no campo do paciente.
                    $chaveSlot                  = $horario->profissional_id . '|' . $slotInicio;
                    // [INICIO CORRECAO] 2026-06-12 - Gerado por Claude (Anthropic)
                    // Finalidade: vago_pos_cancelamento agora recebe o ID do agendamento cancelado de origem (ou null se nao houver), permitindo exibir esse numero na mensagem da grid.
                    $slot->vago_pos_cancelamento = isset($canceladosMap[$chaveSlot]) ? $canceladosMap[$chaveSlot] : null;
                    // [FIM CORRECAO] 2026-06-12

                    if ($qtd_pacientes_horario > 1)
                    {
                        foreach ($pacientes_deste_horario as $pac_id)
                        {
                            $slotMulti                          = clone $slot;
                            $slotIdBaseMulti                    = $slotInicio . '|' . $slotFim . '|' . $horario->profissional_id . '|' . ($horario->ambiente_id ?? '0') . '|' . $pac_id;
                            $slotMulti->id                      = 'SLOT_' . md5($slotIdBaseMulti);
                            $slotMulti->paciente_id             = $pac_id;
                            $slotMulti->multiplos_pacientes     = true;
                            $slots[]                            = $slotMulti;
                        }
                    }
                    elseif ($qtd_pacientes_horario === 1)
                    {
                        $slot->paciente_id          = $pacientes_deste_horario[0];
                        $slot->multiplos_pacientes  = false;
                        $slots[]                    = $slot;
                    }
                    else
                    {
                        $slot->multiplos_pacientes = false;
                        $slots[]                   = $slot;
                    }
                    // FIM CORREÇÃO
                }

                $currentDate->modify('+1 day');
            }
            return $slots;
        }
        catch (Exception $e)
        {
            return [];
        }
    }

    private function bkp01_gerarSlotsAPartirDeHorarios($horariosDisponiveis, $dataInicial, $dataFinal, $agendamentosExistentes = [])
    {
        $slots = [];
        try
        {
            // [INICIO CORRECAO] 2026-06-05 10:00 - Gerado por Claude (Anthropic)

            $agendamentosMap = [];              // Finalidade: Agendamentos com cancelado = 'S' nao devem bloquear o slot do horario.
            $canceladosMap   = [];              // CORREÇÃO: 2026-06-08 - Mapa de cancelados por chave de horario para marcar o slot como vago_pos_cancelamento
            foreach ($agendamentosExistentes as $agendamento)
            {
                if (isset($agendamento->cancelado) && $agendamento->cancelado === 'S')
                {
                    // Registra que existia um cancelado neste horario/profissional para sinalizar o slot gerado
                    $keyCancelado = $agendamento->profissional_id . '|' . $agendamento->horario_inicial;
                    $canceladosMap[$keyCancelado] = true;
                    continue;
                }
                $key = $agendamento->profissional_id . '|' . $agendamento->horario_inicial;
                $agendamentosMap[$key] = true;
            }
            // [FIM CORRECAO] 2026-06-05 10:00

            $profissionaisIds = array_unique(array_column($horariosDisponiveis, 'profissional_id'));
            $profMap = [];
            if (!empty($profissionaisIds))
            {
                $repProf  = new TRepository('SystemUsers');
                $critProf = new TCriteria();
                $critProf->add(new TFilter('id', 'IN', $profissionaisIds));
                $profs = $repProf->load($critProf);
                foreach ($profs as $p)
                {
                    $profMap[$p->id] = $p;
                }
            }

            $horariosIds         = array_unique(array_column($horariosDisponiveis, 'id'));
            $pacientesPorHorario = [];
            if (!empty($horariosIds))
            {
                $repPessoa = new TRepository('Pessoa');
                $critPac   = new TCriteria();
                $critPac->add(new TFilter('horarioatendimento_id', 'IN', $horariosIds));
                $pacs = $repPessoa->load($critPac);
                foreach ($pacs as $p)
                {
                    $pacientesPorHorario[$p->horarioatendimento_id][] = $p->id;
                }
            }

            $ambientesIds = [];
            foreach ($horariosDisponiveis as $h)
            {
                if (!empty($h->ambiente_id)) $ambientesIds[$h->ambiente_id] = true;
            }
            $ambientesIds = array_keys($ambientesIds);

            $ambMap = [];
            if (!empty($ambientesIds))
            {
                $repAmb  = new TRepository('Ambiente');
                $critAmb = new TCriteria();
                $critAmb->add(new TFilter('id', 'IN', $ambientesIds));
                $ambs = $repAmb->load($critAmb);
                foreach ($ambs as $a)
                {
                    $ambMap[$a->id] = $a;
                }
            }

            $inicioPeriodo = new DateTime(substr($dataInicial, 0, 10));
            $fimPeriodo    = new DateTime(substr($dataFinal, 0, 10));
            $fimPeriodo->modify('+1 day');

            $intervaloDias    = $inicioPeriodo->diff($fimPeriodo)->days;
            $limiteMaximoDias = 90;
            if ($intervaloDias > $limiteMaximoDias)
            {
                throw new Exception("Periodo maximo para visualizacao de horarios eh de {$limiteMaximoDias} dias. Ajuste as datas de pesquisa.");
            }

            $currentDate = clone $inicioPeriodo;

            while ($currentDate < $fimPeriodo)
            {
                $diaSemanaAtual = (int)$currentDate->format('w');
                $dataFormatada  = $currentDate->format('Y-m-d');

                foreach ($horariosDisponiveis as $horario)
                {
                    if ((int)$horario->dia_da_semana !== $diaSemanaAtual) continue;
                    if (!in_array($horario->status, ['A', 'B', 'D'])) continue;

                    $horaInicio = substr($horario->hora_inicio, 0, 5) . ':00';
                    $horaFim    = substr($horario->hora_fim, 0, 5) . ':00';
                    $slotInicio = $dataFormatada . ' ' . $horaInicio;
                    $slotFim    = $dataFormatada . ' ' . $horaFim;

                    if (isset($agendamentosMap[$horario->profissional_id . '|' . $slotInicio])) continue;

                    $slot           = new Agendamento();
                    $slotIdBase     = $slotInicio . '|' . $slotFim . '|' . $horario->profissional_id . '|' . ($horario->ambiente_id ?? '0');
                    $slot->id       = 'SLOT_' . md5($slotIdBase);

                    if ($horario->status === 'B')
                    {
                        $slot->tipo_slot       = 'BLOQUEADO';
                        $slot->status          = '99';
                        $slot->motivo_bloqueio = $horario->motivo_bloqueio ?? 'O motivo deste bloqueio nao foi informado';
                    }
                    else
                    {
                        $slot->tipo_slot = 'DISPONIVEL';
                        $slot->status    = 'LIVRE';
                    }

                    $slot->horario_inicial   = $slotInicio;
                    $slot->horario_final     = $slotFim;
                    $slot->profissional_id   = $horario->profissional_id;
                    $slot->empresa_id        = $horario->empresa_id;
                    $slot->ambiente_id       = $horario->ambiente_id ?? null;
                    $slot->cancelado         = 'N';
                    $slot->consulta_retorno  = 'N';
                    $slot->entrega_resultado = 'NE';
                    $slot->ordematendimento  = 0;
                    $slot->horario_status    = $horario->status;

                    if (isset($profMap[$horario->profissional_id]))
                    {
                        $slot->profissional = $profMap[$horario->profissional_id];
                    }

                    if (!empty($horario->ambiente_id) && isset($ambMap[$horario->ambiente_id]))
                    {
                        $slot->ambiente = $ambMap[$horario->ambiente_id];
                    }

                    // Gerado por: Claude Sonnet 4.6 | Data: 2026-06-05
                    // Correção: Quando um horario possui multiplos pacientes vinculados, gera um slot DISPONIVEL por paciente em  linha separada na grid, todos com cor vermelha via propriedade multiplos_pacientes = true. 
                    // Quando ha paciente unico, comportamento original mantido.
                     $pacientes_deste_horario = $pacientesPorHorario[$horario->id] ?? [];
                    $qtd_pacientes_horario   = count($pacientes_deste_horario);

                    // CORREÇÃO: 2026-06-08 - Gerado por Claude (Anthropic)
                    // Finalidade: Marcar o slot como "vago pos cancelamento" quando havia um agendamento  cancelado neste mesmo horario/profissional. Isso permite que a grid exiba o slot na cor laranja com o texto "HORARIO VAGO" no campo do paciente.
                    $chaveSlot                  = $horario->profissional_id . '|' . $slotInicio;
                    $slot->vago_pos_cancelamento = isset($canceladosMap[$chaveSlot]) ? true : false;

                    if ($qtd_pacientes_horario > 1)
                    {
                        foreach ($pacientes_deste_horario as $pac_id)
                        {
                            $slotMulti                          = clone $slot;
                            $slotIdBaseMulti                    = $slotInicio . '|' . $slotFim . '|' . $horario->profissional_id . '|' . ($horario->ambiente_id ?? '0') . '|' . $pac_id;
                            $slotMulti->id                      = 'SLOT_' . md5($slotIdBaseMulti);
                            $slotMulti->paciente_id             = $pac_id;
                            $slotMulti->multiplos_pacientes     = true;
                            $slots[]                            = $slotMulti;
                        }
                    }
                    elseif ($qtd_pacientes_horario === 1)
                    {
                        $slot->paciente_id          = $pacientes_deste_horario[0];
                        $slot->multiplos_pacientes  = false;
                        $slots[]                    = $slot;
                    }
                    else
                    {
                        $slot->multiplos_pacientes = false;
                        $slots[]                   = $slot;
                    }
                    // FIM CORREÇÃO
                }

                $currentDate->modify('+1 day');
            }

            return $slots;
        }
        catch (Exception $e)
        {
            return [];
        }
    }
