<?php

namespace App\DAO;

use App\DAO\DAOResponse\DAOResponse;
use App\DAO\DAOResponse\DAOResponseInterface;
use App\DAO\Order\QueryOrder;
use App\DAO\Pagination\Pagination;
use App\Entities\AgendaMedico\AgendaMedico;
use App\Infra\K2web\K2web;
use App\Mappers\AgendaMedicoMapper;
use App\Models\AgendaMedicoModel;
use App\Services\JWTService;
use App\Services\LogService;
use App\Services\JWT\UsuarioLogado;
use App\Utils\DateUtils;
use App\Utils\Collections\GeneralCollection;
use App\Utils\Collections\QueryFilterCollection;
use App\Utils\Collections\QueryOrderCollection;
use App\ViewModels\AgendaMedico\AgendaMedicoDetailsAgenda\AgendaMedicoDetailAgendaViewModel;
use App\ViewModels\AgendaMedico\AgendaMedicoMultipleRegistrationViewModel\AgendaMedicoMultipleRegistrationViewModel;
use App\ViewModels\AgendaMedico\AgendaMedicoRegistrationViewModel\AgendaMedicoRegistrationViewModel;
use App\ViewModels\AgendaMedico\AgendaMedicoUpdateViewModel\AgendaMedicoUpdateViewModel;
use DateTime;

class AgendaMedicoDAO extends DAO {

	public function deleteAgendaMedico(int $id_agenda) {

		$agenda_model = new AgendaMedicoModel();
		$dao_response = $this->getDAOResponseForDeletion( $agenda_model, $id_agenda );
		return $dao_response;

	}

	public function persistAgendaMedicoRegistrationVM(AgendaMedicoRegistrationViewModel $agenda_registration_vm): DAOResponse {

		$dao_response = new DAOResponse();
		$agenda = $this->persistAgendaMedico( $agenda_registration_vm );
		$error = $this->db->error();
		if ($error && $error ['code']) {
			$status_persistence = $error ['code'];
			$dao_response_errors = [ ];
			if ($status_persistence === DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS) {
				if (strpos( $error ['message'], 'fk_agenda_id_medico' ) !== false) {
					$dao_response_errors ['id_medico'] = 'Esse médico não existe.';
				}
				if (strpos( $error ['message'], 'fk_agenda_id_tipo_procedimento' ) !== false) {
					$dao_response_errors ['procedimento'] = 'Esse procedimento não existe.';
				}
				if (strpos( $error ['message'], 'fk_agenda_adicionado_por' ) !== false) {
					$dao_response_errors ['adicionado_por'] = 'Esse usuário não existe.';
				}
				if (strpos( $error ['message'], 'agenda_ibfk_1' ) !== false) {
					$dao_response_errors ['unidade'] = 'Essa unidade não existe.';
				}
			} else if ($status_persistence === DAOResponseInterface::DUPLICATED_UNIQUE_KEY) {
				if (strpos( $error ['message'], 'AGENDA.AGENDA_codigo_unique_key' ) !== false) {
					$dao_response_errors ['hora_inicio'] = 'Esse horário já está ocupado.';
				}
			}
			$dao_response->setErrors( $dao_response_errors );
			log_message( 'error', json_encode( $error ) );
		} else {
			$status_persistence = !!$agenda->getId() ? DAOResponseInterface::SUCESS : DAOResponseInterface::DATABASE_CONNECTION_ERROR;
		}
		$this->db->close();
		$dao_response->setStatus( $status_persistence );
		if ($status_persistence === DAOResponseInterface::SUCESS) {
			$dao_response->setData( $agenda );
		}
		return $dao_response;

	}

	private function persistAgendaMedico(AgendaMedicoRegistrationViewModel $agenda_registration_vm): AgendaMedico {

		$agenda_model = new AgendaMedicoModel();
		$agenda = AgendaMedicoMapper::getAgendaMedicoFromAgendaMedicoRegistrationVM( $agenda_registration_vm );
		$agenda_model->registerAgendaMedico( $agenda );
		return $agenda;

	}

	public function getAgendaMedicoConflicts(AgendaMedicoRegistrationViewModel $agenda_registration_vm): DAOResponse {

		$sql = "
            SELECT 
                `id`,
                `data`,
                SUBSTRING(`hora_inicio`, 1, 5) as hora_inicio,
                SUBSTRING(`hora_final`, 1, 5) as hora_final
            FROM `agenda`
            WHERE
                :data: = data AND 
                :id_medico: = id_medico AND
                (
                    (:hora_inicio: >= hora_inicio AND :hora_inicio: <  hora_final) OR
                    (:hora_inicio: >= hora_inicio AND :hora_final:  <  hora_final) OR
                    (:hora_inicio: <  hora_inicio AND :hora_inicio: >= hora_final) OR
                    (:hora_inicio: <  hora_inicio AND :hora_final:  >= hora_final) OR
                    (:hora_final:  >= hora_inicio AND :hora_inicio: <  hora_final) OR
                    (:hora_final:  >= hora_inicio AND :hora_final:  <  hora_final) OR
                    (:hora_final:  <  hora_inicio AND :hora_inicio: >= hora_final) OR
                    (:hora_final:  <  hora_inicio AND :hora_final:  >= hora_final)
                )
            ORDER BY hora_inicio
        ";
		$binds = [ 
			'data' => $agenda_registration_vm->getData()
				->format( 'Y-m-d' ),
			'hora_inicio' => $agenda_registration_vm->getHoraInicio()
				->format( 'H:i' ),
			'hora_final' => $agenda_registration_vm->getHoraFinal()
				->format( 'H:i' ),
			'id_medico' => $agenda_registration_vm->getIdMedico()
		];
		$query = $this->db->query( $sql, $binds );
		return $this->getResponseForSchedulesConflictByQuery( $query );

	}

	public function getAgendaMedicoConflictsForMultipleRegistration(AgendaMedicoMultipleRegistrationViewModel $agenda_multiple_registration_vm,
			GeneralCollection $service_dates): DAOResponse {

		$sql = "
            SELECT 
                `id`,
                `data`,
                SUBSTRING(`hora_inicio`, 1, 5) as hora_inicio,
                SUBSTRING(`hora_final`, 1, 5) as hora_final
            FROM `agenda_especialidade`
            WHERE
                `data` in :data_list: AND 
                 id_medico = :id_medico: AND
                hora_final > :hora_inicio: AND 
                hora_inicio < :hora_final:
            ORDER BY `data`, hora_inicio
        ";
		$list_dates = $this->getConflictsListDates( $service_dates );
		$sql = str_replace( ':data_list:', $list_dates, $sql );
		$sql = trim( preg_replace( '/\s{2,}/', ' ', $sql ) );
		$binds = [ 
			'id_medico' => $agenda_multiple_registration_vm->getIdMedico(),
			'hora_inicio' => $agenda_multiple_registration_vm->getHoraInicio()
				->format( 'H:i' ),
			'hora_final' => $agenda_multiple_registration_vm->getHoraFim()
				->format( 'H:i' )
		];
		$query = $this->db->query( $sql, $binds );
		return $this->getResponseForSchedulesConflictByQuery( $query );

	}

	// Método usado tanto na obtenção de conflitos para horários individuais quanto para o cadastro múltiplo.
	private function getResponseForSchedulesConflictByQuery($query): DAOResponse {

		$result_array = $query->getResultArray();
		$error = $this->db->error();
		$dao_response = new DAOResponse();
		if ($error && $error ['code']) {
			$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
		} else {
			if (count( $result_array )) {
				$dao_response->setStatus( DAOResponseInterface::SUCESS );
				$agenda_conflictig_vm_collection = AgendaMedicoMapper::getAgendaMedicoConflictingVMCollectionFromArray( $result_array );
				$dao_response->setData( $agenda_conflictig_vm_collection );
			} else {
				$dao_response->setStatus( DAOResponseInterface::RECORD_NOT_FOUND );
			}
		}
		return $dao_response;

	}

	private function getConflictsListDates(GeneralCollection $service_dates) {

		$mapper = function (DateTime $date) {
			return '\'' . $date->format( 'Y-m-d' ) . '\'';
		};
		$filter = $service_dates->map( $mapper )
			->join( ',' );
		return "($filter)";

	}

	public function getAgendaMedicoList(QueryFilterCollection $query_filter_collection, QueryOrderCollection $query_order_collection,
			?Pagination $pagination): DAOResponse {

		$sql = <<<SQL
		    SELECT
		        agenda_especialidade.id AS id,
		        agenda_especialidade.`data` as data,
		        agenda_especialidade.id_medico as id_medico,
		        agenda_especialidade.id_unidade as id_unidade,
		        agenda_especialidade.id_especialidade as id_especialidade,
		        especialidade.descricao as nome_especialidade,
		        SUBSTRING(agenda_especialidade.hora_inicio, 1, 5) as hora_inicio,
		        SUBSTRING(agenda_especialidade.hora_final, 1, 5) as hora_final,
		        pessoa_fisica.nome as nome_paciente,
		        agendamento.id as id_agendamento,
		        agendamento.id_paciente as id_paciente,
		        agendamento.id_paciente as id_cliente,
		        IF(consulta.id IS NULL, 0, consulta.id) as id_consulta,
		        agendamento.cancelado_em as cancelado_em,
		        pessoa_fisica.nome as nome_cliente,
		        pet.numero_chip as nome_pet
		    FROM agenda_especialidade
		    LEFT JOIN agendamento ON agenda_especialidade.id = agendamento.id_agenda
		    LEFT JOIN consulta ON consulta.id_agendamento = agendamento.id
		    LEFT JOIN paciente ON agendamento.id_paciente = paciente.id
		    LEFT JOIN pessoa ON paciente.id_pessoa = pessoa.id
		    LEFT JOIN usuario ON pessoa.id = usuario.id_pessoa
		    LEFT JOIN pessoa_fisica ON pessoa_fisica.id_pessoa = pessoa.id
		    LEFT JOIN cliente ON cliente.id_pessoa = pessoa.id 
		    LEFT JOIN pet_cliente ON pet_cliente.id_cliente = cliente.id 
		    LEFT JOIN pet ON pet.id = pet_cliente.id_pet
		    INNER JOIN especialidade ON agenda_especialidade.id_especialidade = especialidade.id
			WHERE 1=1 
		SQL;
		$jwt_payload = JWTService::getTokenJWTPayload();
		if ($jwt_payload->getProfile() === PERFIL_PACIENTE) {
			$sql .= "
			        AND (usuario.`id` = {$jwt_payload->getUid()} OR usuario.`id` IS NULL)
			";
		}
		if ($jwt_payload->getProfile() === PERFIL_ASSISTENTE) {
			$sql .= "
			        AND (agendamento.`adicionado_por` = {$jwt_payload->getUid()} OR agendamento.`adicionado_por` IS NULL)
			";
		}
		if ($jwt_payload->getProfile() === PERFIL_MEDICO) {
			$query = db_connect()->query( 
				"
				SELECT
					m.id as medico_id
				FROM usuario u
				inner join pessoa p on p.id = u.id_pessoa 
				inner join profissional p2 on p2.id_pessoa  = p.id
				inner join medico m on m.id_profissional = p2.id
				where
					u.id = :usuario_id:
				limit 1
			", [ 
					'usuario_id' => UsuarioLogado::getUsuarioId()
				] )
				->getResultArray();
			$medicoId = count( $query ) ? ( int ) $query [0] ['medico_id'] : 0;
			$sql .= " AND (agenda.id_medico = $medicoId OR agenda.id_medico is null)";
		}
		$query_order_collection->push( QueryOrder::builder()->setTable( 'agendamento' )
			->setNameParam( 'cancelado_em' )
			->setDesc( false )
			->build() );
		return $this->getDAOResponseFromPaginatedSelect( [ ], $pagination, $query_filter_collection, $query_order_collection, $sql,
			function (array $result_array) {
				return AgendaMedicoMapper::getAgendaMedicoListingVMCollectionFromArray( $result_array );
			} );

	}

	public function persistAgendaMedicoCollection(GeneralCollection $agenda_collection, bool $encaixe): DAOResponse {

		$agenda_model = new AgendaMedicoModel();
		$this->db->transStart();
		$insert_status = $agenda_model->registerAgendaMedicoCollection( $agenda_collection, $encaixe );
		$error = $this->db->error();
		$dao_response = new DAOResponse();
		if ($error && $error ['code']) {
			$dao_response->setStatus( $error ['code'] );
			$dao_response->setErrors( $this->agendaInsertErrors( $error ) );
		} else {
			if ($insert_status) {
				$this->db->transComplete();
				$dao_response->setStatus( DAOResponseInterface::SUCESS );
			} else {
				$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
			}
		}
		return $dao_response;

	}

	private function agendaInsertErrors(array $error): array {

		$dao_response_errors = [ ];
		if ($error ['code'] === DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS) {
			if (strpos( $error ['message'], 'fk_agenda_id_medico' ) !== false) {
				$dao_response_errors ['id_medico'] = 'Esse médico não existe.';
			}
			if (strpos( $error ['message'], 'fk_agenda_id_tipo_procedimento' ) !== false) {
				$dao_response_errors ['procedimento'] = 'Esse tipo de procedimento não existe.';
			}
			if (strpos( $error ['message'], 'fk_agenda_adicionado_por' ) !== false) {
				$dao_response_errors ['adicionado_por'] = 'Esse usuário não existe.';
			}
			if (strpos( $error ['message'], 'agenda_ibfk_1' ) !== false) {
				$dao_response_errors ['unidade'] = 'Essa unidade não existe.';
			}
		} else if ($error ['code'] === DAOResponseInterface::DUPLICATED_UNIQUE_KEY) {
			if (strpos( $error ['message'], 'AGENDA.AGENDA_codigo_unique_key' ) !== false) {
				$dao_response_errors ['hora_inicio'] = 'Esse horário já está ocupado.';
			}
		}
		return $dao_response_errors;

	}

	public function updateAgendaMedico(int $id_agenda, AgendaMedicoUpdateViewModel $agenda_update_vm): DAOResponse {

		$agenda_model = new AgendaMedicoModel();
		$agenda = AgendaMedicoMapper::getAgendaMedicoFromAgendaMedicoUpdateVM( $agenda_update_vm );
		$query_result = $agenda_model->updateAgendaMedicoByID( $id_agenda, $agenda );
		$error = $this->db->error();
		$this->db->close();
		$dao_response = new DAOResponse();
		if ($error && $error ['code']) {
			$status_response = $error ['code'];
			$dao_response_errors = [ ];
			if ($status_response === DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS) {
				if (strpos( $error ['message'], 'fk_agenda_id_tipo_procedimento' ) !== false) {
					$dao_response_errors ['id_tipo_procedimento'] = 'Esse tipo de procedimento não existe.';
				}
				if (strpos( $error ['message'], 'fk_agenda_alterado_por' ) !== false) {
					$dao_response_errors ['alterado_por'] = 'Esse usuário não existe.';
				}
			}
			$dao_response->setErrors( $dao_response_errors );
			(new LogService())->log_error( $status_response, "Ocorreu um erro durante a atualização da agenda $id_agenda", [ 
				$agenda->toArray(),
				$error
			] );
		} else {
			$status_response = $query_result ? DAOResponseInterface::SUCESS : DAOResponseInterface::RECORD_NOT_FOUND;
		}
		$dao_response->setStatus( $status_response );
		return $dao_response;

	}

	public function getAgendaMedicoByID(int $id_agenda): DAOResponse {

		$sql = '
            SELECT
                id,
                codigo,
                id_medico,
                `data`,
                SUBSTRING(hora_inicio, 1, 5) as hora_inicio,
                SUBSTRING(hora_final, 1, 5) as hora_final,
                id_tipo_procedimeno
            FROM
                agenda
            WHERE 
                  id = :id_agenda:
            LIMIT 1
        ';
		$binds = [ 
			'id_agenda' => $id_agenda
		];
		$query_filter_collection = new QueryFilterCollection();
		return $this->getDAOResponseFromUniqueRowSelect( $binds, $query_filter_collection, $sql,
			function (array $result_array): AgendaMedico {
				return AgendaMedicoMapper::getAgendaMedicoByArray( $result_array [0] );
			} );

	}

	public function registerNewAgendamentoMedicoByRecepcao(int $idAgenda, int $idProcedimento, int $idPet, int $idStatus, string $idObservacao, $consultaName, $whatsapp): DAOResponse {

		$dao_response = new DAOResponse();

        // CAPTURA ESPECIALIDADE
        $sqlIdEspecialidade = <<<SQL
		   select id_especialidade, id_medico, hora_inicio, hora_final, data from agenda_especialidade where id = :id_agenda:
		SQL;

        $conn = db_connect();

        $sqlResultEspecialidade = $conn->query( $sqlIdEspecialidade,
            [
                'id_agenda' => $idAgenda
            ] )
            ->getResultArray();

        $sqlTipoProcedimento = <<<SQL
		   select id_tipo_procedimento from procedimento where id = :id_procedimento:
		SQL;

        $sqlResultTipoProcedimento= $conn->query( $sqlTipoProcedimento,
            [
                'id_procedimento' => $idProcedimento
            ] )
            ->getResultArray();

        $usr = UsuarioLogado::getUsuarioId();
        // CAPTURA CONVENIO PROCEDIMENTO DE UMA UNIDADE TELEVET COM CONVENIO PUBLICO
        $sqlConvenioProcedimento = <<<SQL
		   select 
		        cp.id as id_convenio_procedimento, 
		        cp.id_convenio, 
		        ep.id_procedimento, 
		        ep.id_especialidade  
		    from convenio_procedimento cp , convenio c , especialidade_procedimento ep 		       
		    where
		        c.id_tipo_convenio = 2 
		        and cp.id_unidade = :id_unidade: 
		        and cp.id_procedimento = :id_procedimento:
				and cp.id_convenio = c.id
				and cp.id_procedimento = ep.id_procedimento
				and ep.id_especialidade = :id_especialidade:	
		SQL;



        $especialidadeProcedimento = $conn->query( $sqlConvenioProcedimento,
            [
                'id_unidade' => 24,
                'id_procedimento' => $idProcedimento,
                'id_especialidade' => $sqlResultEspecialidade[0]['id_especialidade']
            ] )
            ->getResultArray();


        $convenioProceimentoId = count( $especialidadeProcedimento ) ? $especialidadeProcedimento [0] ['id_convenio_procedimento'] : 0;
        $convenioId = count( $especialidadeProcedimento ) ? $especialidadeProcedimento [0] ['id_convenio'] : 0;
        $especialidadeId = count( $especialidadeProcedimento ) ? $especialidadeProcedimento [0] ['id_especialidade'] : 0;
        $conn->transBegin();



        // Cria a requisição do procedimento
        $sql = <<<SQL
		    INSERT INTO requisicao_procedimento 
			(id_medico, id_convenio_procedimento, id_consulta, status_id, usuario_executor, id_requisitante, data_requisicao, hora_requisicao, data_execucao, hora_execucao, id_pet)
		    VALUES (
		        :id_medico:,
		        :id_convenio_procedimento:,
		        :id_consulta:,
		        :status_id:,
		        :usuario_executor:,
				:id_requisitante:,
		        :data_requisicao:,
		        :hora_requisicao:,
		        :data_execucao:,
		        :hora_execucao:,
		        :id_pet:
		    )
		SQL;

        $conn->query( $sql,
            [
                'id_medico' => $sqlResultEspecialidade[0]['id_medico'],
                'id_convenio_procedimento' => ( int ) $convenioProceimentoId,
                'id_consulta' => null,
                'status_id' => 1, // CRIADO
                'usuario_executor' => null,
                'id_requisitante' => $usr,
                'data_requisicao' => date( 'Y-m-d' ),
                'hora_requisicao' => date( 'H:i:s' ),
                'data_execucao' => '1900-01-01',
                'hora_execucao' => '00:00:00',
                'id_pet' => ( int ) $_POST ['id_pet']
            ] )->numRows;
        $requisicaoId = $conn->insertID();
        $error = $conn->error();

        if ($error ['code']) {
            die( 'Ocorreu um erro ao criar a requisição' );
        }

        // Finaliza criação da requisição de um procedimento

        // Cria a comanda e gera item pra comanda
        $requisicaoProcedimentoId = $requisicaoId;

        // CRIAÇÃO DE SALA PARA REQUISICAO PROCEDIMENTO
        $curl = curl_init();
        curl_setopt_array( $curl,
            array (
                CURLOPT_URL => 'https://televplay.eadmeeting.com/api/v2/Meeting',
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_ENCODING => '',
                CURLOPT_MAXREDIRS => 10,
                CURLOPT_TIMEOUT => 0,
                CURLOPT_FOLLOWLOCATION => true,
                CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
                CURLOPT_CUSTOMREQUEST => 'POST',
                CURLOPT_POSTFIELDS => 'url=' . $consultaName . '&name=Participante&description=Teleconsulta&tipo=meeting',
                CURLOPT_HTTPHEADER => array (
                    'Authorization: OTRhNWQ3YjY1NzRmZDU0Y2I4OTY4NDRlMjk2NGQ4M2QwYTRlYjM3Yw==',
                    'Content-Type: application/x-www-form-urlencoded',
                    'Cookie: PHPSESSID=thc4lcitp13cb16c0qduumqod5'
                )
            ) );
        $video = json_decode( curl_exec( $curl ), true );

        curl_close( $curl );
        if ($video ['status'] == "success") {
            $sql = 'INSERT INTO requisicao_sala_video (
                    id_requisicao_procedimento,
                    id_meeting,
                    name_meeting,
                    description_meeting,
                    url_meeting,
                    tipo_meeting,
                    status_meeting,
                    tipoacesso_meeting,
                    stream_name_meeting,
                    live_stream_id_meeting
                ) VALUES ();
        ';
            $today = DateUtils::today();

            $values = "(
            {$requisicaoProcedimentoId},   
            {$video['meeting']['id']},   
            '{$video['meeting']['name']}',   
            '{$video['meeting']['description']}',   
            '{$video['meeting']['url']}',   
            '{$video['meeting']['tipo']}',   
            '{$video['meeting']['status']}',   
            '{$video['meeting']['tipoacesso']}',   
            '{$video['meeting']['stream_name']}',   
            '{$video['meeting']['live_stream_id']}'
        )";
            $sql = str_replace( 'VALUES ()', "VALUES $values", $sql );
            $query = $conn->query( $sql );
        } else {
            die("Não foi possível criar a sua sala de vídeo.");
        }

		$sql = <<<SQL
			insert into agendamento_especialidade
			(id_agenda_especialidade, id_pet, id_procedimento , criado_por, data_criacao, hora_criacao, id_status , observacao, id_requisicao_procedimento) values
			(:agenda_id:, :pet_id:, :procedimento_id:, :user_id:, :datacr: , :hora: , :idstatus: , :idobservacao:, :id_requisicao_procedimento:)
		SQL;

		$usr = UsuarioLogado::getUsuarioId();
		$dt = DateUtils::today();
		$hr = DateUtils::now_time();
		$binds = [ 
			'agenda_id' => $idAgenda,
			'pet_id' => $idPet,
			'procedimento_id' => $idProcedimento,
			'user_id' => $usr,
			'datacr' => $dt,
			'hora' => $hr,
			'idstatus' => $idStatus,
			'idobservacao' => $idObservacao,
            'id_requisicao_procedimento' => $requisicaoId
		];

		$conn->query( $sql, $binds );
		$errors = $conn->error();

		if ($errors ['code'] != 0) {
			$dao_response->setErrors( $errors );
			if (substr( strtolower( trim( $errors ['message'] ) ), 0, 15 ) == 'duplicate entry') {
				$dao_response->setStatus( DAOResponseInterface::DUPLICATED_UNIQUE_KEY );
			} else {
				$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
			}
			$dao_response->setData( [ ] );
			return $dao_response;
		}

		$newid = $conn->insertID();



        $sqlInsertAgenda = <<<SQL
			    insert into agenda (codigo,id_medico,data,hora_inicio, hora_final, id_tipo_procedimento, adicionado_por, alterado_por, criado_em,alterado_em,id_unidade, id_assistente)
			    values (
			        :codigo:,
			        :id_medico:,
			        :data:,
			        :hora_inicio:,
			        :hora_final:,
			        :id_tipo_procedimento:,
			        :adicionado_por:,
			        :alterado_por:,
			        :criado_em:,
			        :alterado_em:,
			        :id_unidade:, 
			        :id_assistente:	
			    )
			SQL;
        $currdate = date( 'Y-m-d' );
        $bindsInsertAgenda = [
            'codigo' => 'M'. $sqlResultEspecialidade[0]['id_medico'] . '-U' . 24 . '-' . $sqlResultEspecialidade[0]['data'] . $sqlResultEspecialidade[0]['hora_inicio'] . $sqlResultEspecialidade[0]['hora_final'],
            'id_medico' => $sqlResultEspecialidade[0]['id_medico'],
            'data' => $sqlResultEspecialidade[0]['data'],
            'hora_inicio' => $sqlResultEspecialidade[0]['hora_inicio'],
            'hora_final' => $sqlResultEspecialidade[0]['hora_final'],
            'id_tipo_procedimento' => $sqlResultTipoProcedimento[0]['id_tipo_procedimento'],
            'adicionado_por' => UsuarioLogado::getUsuarioId(),
            'alterado_por' => null,
            'criado_em' => date( 'Y-m-d H:i:s' ),
            'alterado_em' => null,
            'id_unidade' => 24,
            'id_assistente' => null
        ];
        $conn->query( $sqlInsertAgenda, $bindsInsertAgenda );
        $agendaId = $conn->insertID();
        $status = $conn->transStatus();

        if(!$status){
            $conn->transRollback();
            $dao_response->setErrors( $errors );
            $dao_response->setStatus( DAOResponseInterface::UNIQUE_KEY_VIOLATED );
            return $dao_response;
        }


        $sqlDadosPaciente = <<<SQL
		    SELECT 
		        p.id as pet_id,
		        c.id as cliente_id,
		        pac.id as paciente_id
		    from pet p
		    inner join pet_cliente pc on pc.id_pet = p.id 
		    inner join cliente c on c.id = pc.id_cliente 
		    inner join pessoa pes on pes.id = c.id_pessoa 
		    inner join paciente pac on pac.id_pessoa = pes.id 
		    WHERE 
		        p.id = :pet_id:
		    limit 1
		SQL;

        $binds = [
            'pet_id' => $idPet
        ];

        $resultDadosPaciente = $conn->query( $sqlDadosPaciente, $binds )
            ->getResultArray();


        $agendamentoCriacaoSql = <<<SQL
			insert into agendamento 
				(id_agenda, id_cliente, id_paciente, id_procedimento, id_requisicao,  
				codigo, cancelado_em, adicionado_por, alterado_por, alterado_em, criado_em, atrasado, numero_notificacao_agendamento)
			values 
				( :id_agenda:, :id_cliente:, :id_paciente:, :id_procedimento:, :id_requisicao:,
				:codigo:, :cancelado_em:, :adicionado_por:, :alterado_por:, :alterado_em:,
				:criado_em:,:atrasado:, :numero_notificacao_agendamento:)
		SQL;

        $binds = [
            'id_agenda' => $agendaId,
            'id_cliente' => $resultDadosPaciente[0]['cliente_id'],
            'id_paciente' => $resultDadosPaciente[0]['paciente_id'],
            'id_procedimento' => $idProcedimento,
            'id_requisicao' => $requisicaoId,
            'codigo' => $agendaId . '_' . date( 'Ymd' ) . ' ' . '00:00:00',
            'cancelado_em' => null,
            'adicionado_por' => UsuarioLogado::getUsuarioId(),
            'alterado_por' => null,
            'alterado_em' => null,
            'criado_em' => date( 'Y-m-d H:i:s' ),
            'atrasado' => 0,
            'numero_notificacao_agendamento' => $whatsapp
        ];

        $conn->query( $agendamentoCriacaoSql, $binds );
        $agendamentoId = $conn->insertID();

        $sqlAgendamentoPet = <<<SQL
		    insert into agendamento_pet (id_agendamento, id_pet)
		    values (:id_agendamento:, :id_pet:)
		SQL;

        $conn->query( $sqlAgendamentoPet, [
            'id_agendamento' => $agendamentoId,
            'id_pet' => $idPet
        ] );

        $conn->transCommit();

        $dao_response->setStatus( DAOResponseInterface::SUCESS );
        $dao_response->setData( [
            'id' => $newid,
            'agendamento_especialidade' => $newid,
            'agendamento' => $agendamentoId
        ] );

        #$mensagem = new K2web();
        #$mensagem->enviarMensagem(1, $agendamentoId);


        return $dao_response;

	}

	public function updateStatusAgendamentoMedico(int $idAgenda, int $idStatus, string $observacao): DAOResponse {

		$dao_response = new DAOResponse();

		$sql = <<<SQL
			update agendamento_especialidade
			set id_status = :idstatus: , atualizado_por = :user_id: , 
				data_atualizacao = :dataatz: , hora_atualizacao = :hora:,
				observacao = :obs:
			where  id_agenda_especialidade = :agenda_id: 
		SQL;

		$usr = UsuarioLogado::getUsuarioId();
		$dt = DateUtils::today();
		$hr = DateUtils::now_time();
		$binds = [ 
			'agenda_id' => $idAgenda,
			'user_id' => $usr,
			'dataatz' => $dt,
			'hora' => $hr,
			'idstatus' => $idStatus,
			'obs' => $observacao
		];

		$conn = db_connect();
		$conn->query( $sql, $binds );
		$errors = $conn->error();
		$affectRows = $conn->affectedRows();

		if ($errors ['code'] != 0) {
			$dao_response->setErrors( $errors );
			$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
			return $dao_response;
		}

		if ($affectRows == 0) {
			$dao_response->setStatus( DAOResponseInterface::RECORD_NOT_FOUND );
		} else {
			$dao_response->setStatus( DAOResponseInterface::SUCESS );
		}

		$dao_response->setData( [ ] );

		return $dao_response;

	}

	public function getAgendaUnidadeByMedico(int $especialidade, int $unidade, string $data_inicial, string $data_final, Pagination $pagination): DAOResponse {

		$dao_response = new DAOResponse();
		$page = $pagination->getCurrentPage();
		$itens_per_page = $pagination->getPerPage();
		$offset = ($page - 1) * $itens_per_page;

		$sql_count = <<<SQL
			select count(*) as total				
			from agenda_especialidade ae , especialidade e
			where
				ae.id_especialidade = :id_especialidade:
				and ae.id_especialidade = e.id
				and ae.id_unidade = :id_unidade:
				and ae.`data` >= :data_inicial:
				and ae.`data` <= :data_final:			
		SQL;

		$sql_datas_agenda = <<<SQL
			select 
				ae.id , ae.`data` , ae.hora_inicio , ae.hora_final ,
				ae.id_especialidade , e.descricao as nome_especialidade, ae.id_unidade
			from agenda_especialidade ae , especialidade e 
			where
				ae.id_especialidade = :id_especialidade:
				and ae.id_especialidade = e.id
				and ae.id_unidade = :id_unidade:
				and ae.`data` >= :data_inicial:
				and ae.`data` <= :data_final:
			order by ae.`data` asc
			limit :offset: , :num_itens:
			
		SQL;

		$binds_datas_agenda = [ 
			'id_especialidade' => $especialidade,
			'id_unidade' => $unidade,
			'data_inicial' => $data_inicial,
			'data_final' => $data_final,
			'offset' => $offset,
			'num_itens' => $itens_per_page
		];

		$conn = db_connect();

		$resp_count = $conn->query( $sql_count, $binds_datas_agenda )
			->getResultArray();
		$errors_count = $conn->error();
		if ($errors_count ['code'] != 0) {
			$dao_response->setErrors( $errors_count );
			$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
			$dao_response->setData( [ ] );
			return $dao_response;
		}

		$num_rows = $resp_count [0] ['total'];

		$pagination->setNumRows( $num_rows );

		$resp_datas = $conn->query( $sql_datas_agenda, $binds_datas_agenda )
			->getResultArray();
		$errors_datas = $conn->error();

		if ($errors_datas ['code'] != 0) {
			$dao_response->setErrors( $errors_datas );
			$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
			$dao_response->setData( [ ] );
			return $dao_response;
		}
		// {"num_rows":14,"current_page":1,"first_page":1,"last_page":1,"previous_page":null,"next_page":null,"per_page":15}},
		$dao_response->setStatus( DAOResponseInterface::SUCESS );
		$dao_response->setData( 
			[ 
				'dados' => $resp_datas,
				'paginacao' => [ 
					'current_page' => $pagination->getCurrentPage(),
					'num_rows' => $pagination->getNumRows(),
					'first_page' => $pagination->calculateFirstPage(),
					'last_page' => $pagination->calculateLastPage(),
					'previous_page' => $pagination->calculatePreviousPage(),
					'next_page' => $pagination->calculateNextPage(),
					'per_page' => $pagination->getPerPage()
				]
			] );
		return $dao_response;

	}

	public function getAgendaMedicoByUnidadeByPeriodo(int $unidade, int $especialidade, string $data_inicial): DAOResponse {

		$dao_response = new DAOResponse();

		$sql_datas_agenda = <<<SQL
			select distinct ae.`data`
			from agenda_especialidade ae
			where
				ae.id_especialidade = :id_especialidade:
				and ae.id_unidade = :id_unidade:
				and ae.`data` >= :data_inicial:	
			order by ae.`data` asc
			limit 6
		
		SQL;

		$binds_datas_agenda = [ 
			'id_especialidade' => $especialidade,
			'id_unidade' => $unidade,
			'data_inicial' => $data_inicial
		];

		$conn = db_connect();
		$resp_datas = $conn->query( $sql_datas_agenda, $binds_datas_agenda )
			->getResultArray();
		$errors_datas = $conn->error();

		if ($errors_datas ['code'] != 0) {
			$dao_response->setErrors( $errors_datas );
			$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
			$dao_response->setData( [ ] );
			return $dao_response;
		}

		$agendas = array ();
		$proxima_data = '';

		for($i = 0; $i < count( $resp_datas ); $i++) {

			$dt_inicio = $resp_datas [$i] ['data'];
			if ($i == 5) {
				$proxima_data = $dt_inicio;
				break;
			}

			$sql_horarios = <<<SQL
				select 
					q.id, 
					q.id_status, 
					q.id_unidade, 
					q.id_especialidade,
					q.procedimento_id,
					q.id_medico,
					q.nome_medico,
					case
						when q.procedimento_id > 0 then (select descricao from procedimento where id = q.procedimento_id )
						else ''
					end as procedimento_nome,
					q.pet_id,
					case
						when q.pet_id > 0 then (select pet.nome from pet where pet.id = q.pet_id )
						else ''
					end as pet_nome,
					case
						when q.pet_id > 0 then (select pc.id_cliente  from pet_cliente pc where pc.id_pet = q.pet_id )
						else 0
					end as cliente_id ,
					case
						when q.pet_id > 0 then
							(select pf.nome  from pet_cliente pc ,cliente c ,pessoa_fisica pf
							where pc.id_pet = q.pet_id and pc.id_cliente =c.id and c.id_pessoa = pf.id_pessoa )
						else ''
					end as cliente_nome , 
					q.data, q.hora_inicio, q.hora_final, q.encaixe,
					q.id_agenda_especialidade, q.id_requisicao_procedimento,
					q.id_pet, q.id_cliente,
					DATE_FORMAT(q.data_criacao,'%d/%m/%Y') as data_criacao, q.hora_criacao,
					case
						when q.id_requisicao_procedimento > 0 then
								(select distinct prc.descricao
									from requisicao_procedimento rp, convenio_procedimento cp, procedimento prc
									where rp.id = q.id_requisicao_procedimento 
										and rp.id_convenio_procedimento = cp.id 
										and cp.id_procedimento = prc.id )
						else ''
					end as nome_procedimento,
					case
						when q.id_pet > 0 then (select pet.nome from pet where pet.id = q.id_pet )
						else ''
					end as nome_pet,
					case
						when q.id_cliente > 0 then
							(select pf.nome from cliente c, pessoa_fisica pf 
								where c.id = q.id_cliente and c.id_pessoa = pf.id_pessoa  )
						else ''
					end as nome_cliente,
					case
						when q.pet_id > 0 then
							(select CONCAT( COALESCE (p.telefone_1,'') ," / ", COALESCE (p.telefone_2,'')," / ", COALESCE (p.telefone_3,'')  ) as telefone
								from pet_cliente pc ,cliente c ,pessoa_fisica pf, pessoa p
								where pc.id_pet = q.pet_id and pc.id_cliente =c.id and c.id_pessoa = pf.id_pessoa and pf.id_pessoa = p.id)
						else ''
					end as telefone,
				    case
						when q.pet_id > 0 then
							(select pf.nome from usuario u  
							    inner join pessoa_fisica pf on pf.id_pessoa = u.id_pessoa
							    where q.criado_por = u.id)
						else ''
					end as usuario_agendamento,
					q.observacao
			from  ( select distinct ae.id ,ae.id_unidade , ae.id_especialidade ,
						COALESCE (age.id_procedimento,0) as procedimento_id ,
						COALESCE (age.id_pet,0) as pet_id , 
						ae.`data`, ae.hora_inicio,	ae.hora_final, ae.encaixe,
						COALESCE (age.id_agenda_especialidade,0) as id_agenda_especialidade,
						COALESCE (age.id_status,0) as id_status,
						COALESCE (age.id_requisicao_procedimento,0) as id_requisicao_procedimento,
						COALESCE (age.criado_por,0) as criado_por,
						COALESCE (age.data_criacao,0) as data_criacao,
						COALESCE (age.hora_criacao,0) as hora_criacao,
						ae.id_medico as id_medico,
						pf2.nome as nome_medico,
						case
							when age.id_requisicao_procedimento is not null then 
								( select id_pet from requisicao_procedimento where id = age.id_requisicao_procedimento )
							else 0
						end as id_pet,
						case
							when age.id_requisicao_procedimento is not null then 
								( select pc.id_cliente from requisicao_procedimento rp, pet_cliente pc
									where rp.id = age.id_requisicao_procedimento and rp.id_pet = pc.id_pet )
							else 0
						end as id_cliente,
						COALESCE (age.observacao,'') as observacao
					from agenda_especialidade ae 
					    left join agendamento_especialidade age on age.id_agenda_especialidade = ae.id
					    inner join medico m on ae.id_medico = m.id 
					    inner join profissional p2 on m.id_profissional = p2.id 
					    inner join pessoa_fisica pf2 on p2.id_pessoa = pf2.id_pessoa 
					where
						ae.id_especialidade = :especialidade: and ae.id_unidade = :unidade:	and ae.`data` = :data_inicio: AND (ae.`data` <> CURDATE() OR ae.hora_inicio > DATE_ADD(NOW(), INTERVAL 1 HOUR)) ) as q
			where q.id_status = 0
			order by q.hora_inicio  asc
			
			SQL;

			$binds_horarios = [ 
				'especialidade' => $especialidade,
				'unidade' => $unidade,
				'data_inicio' => $dt_inicio
			];

			$conn_horarios = db_connect();
			$resp_horarios = $conn_horarios->query( $sql_horarios, $binds_horarios )
				->getResultArray();
			$errors_horarios = $conn_horarios->error();

			if ($errors_horarios ['code'] == 0) {
				$horarios = array ();
				for($y = 0; $y < count( $resp_horarios ); $y++) {
					$item = $resp_horarios [$y];
					$obj ['='] = AgendaMedicoDetailAgendaViewModel::builder()->setId( $item ['id'] )
						->setIdUnidade( $item ['id_unidade'] )
						->setIdEspecialidade( $item ['id_especialidade'] )
						->setIdStatus( $item ['id_status'] )
						->setPetId( $item ['pet_id'] )
						->setData( $item ['data'] )
						->setHoraInicio( $item ['hora_inicio'] )
						->setHoraFinal( $item ['hora_final'] )
						->setIdAgendaEspecialidade( $item ['id_agenda_especialidade'] )
						->setIdRequisicaoProcedimento( $item ['id_requisicao_procedimento'] )
						->setIdPet( $item ['id_pet'] )
						->setIdCliente( $item ['id_cliente'] )
						->setPetNome( $item ['pet_nome'] )
						->setClienteNome( $item ['cliente_nome'] )
						->setTelefone( $item ['telefone'] )
						->setObservacao( $item ['observacao'] )
						->setEncaixe( $item ['encaixe'] )
						->build();
					array_push( $horarios, $item );
				}
				$agenda = [ 
					'dia' => $resp_datas [$i] ['data'],
					'horarios' => $horarios
				];
				array_push( $agendas, $agenda );
			} else {
				$dao_response->setErrors( $errors_horarios );
				$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
				$dao_response->setData( [ ] );
				return $dao_response;
			}
		}
		$dao_response->setStatus( DAOResponseInterface::SUCESS );
		$dao_response->setData( [ 
			'proxima_data' => $proxima_data,
			'agenda' => $agendas
		] );
		return $dao_response;

	}


    public function reagendarAgendamentoMedicoByRecepcao(int $idAgendaAntiga, $idAgendaNova): DAOResponse {

        $dao_response = new DAOResponse();

        $sql = <<<SQL
			update agendamento_especialidade set id_agenda_especialidade = :agenda_nova:
			where id_agenda_especialidade = :agenda_antiga:
		SQL;

        $binds = [
            'agenda_antiga' => $idAgendaAntiga,
            'agenda_nova' => $idAgendaNova,
        ];

        $conn = db_connect();
        $conn->query( $sql, $binds );
        $errors = $conn->error();

        if ($errors ['code'] != 0) {
            $dao_response->setErrors( $errors );
            if (substr( strtolower( trim( $errors ['message'] ) ), 0, 15 ) == 'duplicate entry') {
                $dao_response->setStatus( DAOResponseInterface::DUPLICATED_UNIQUE_KEY );
            } else {
                $dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
            }
            $dao_response->setData( [ ] );
            return $dao_response;
        }
        $dao_response->setStatus( DAOResponseInterface::SUCESS );
        return $dao_response;

    }

    public function getAgendaDateList (?Pagination $pagination, ?QueryFilterCollection $query_filter_collection,
                                       ?QueryOrderCollection $query_order_collection = null, $id_procedimento, $data_agenda, $id_unidade) : DAOResponse {
        $sql = '
               SELECT 
                    a1.id_especialidade,
                    a1.`data`,
                    a1.id as idagenda, 
                    SUBSTRING(a1.hora_inicio, 1, 5) as hora_inicio,
                    SUBSTRING(a1.hora_final, 1, 5) as hora_final 
               FROM agenda_especialidade a1 
               INNER JOIN especialidade_procedimento ep on a1.id_especialidade = ep.id_especialidade
               LEFT JOIN agendamento_especialidade a2 on a1.id  = a2.id_agenda_especialidade
               WHERE 
                   a1.id_unidade = :id_unidade: and 
                   a1.`data` = :data_agenda: and
                   ep.id_procedimento = :id_procedimento: and
                   a2.id_agenda_especialidade is null
        ';
        $binds = ['data_agenda' => $data_agenda, 'id_procedimento' => $id_procedimento, 'id_unidade' => $id_unidade];
        return $this->getDAOResponseFromPaginatedSelect($binds, $pagination, $query_filter_collection, $query_order_collection, $sql, function (array $result_array) {
            return AgendaMedicoMapper::getAgendaDateVMCollectionFromArray($result_array);
        });
    }

}
