<?php

namespace App\DAO;

use App\DAO\DAOResponse\DAOResponse;
use App\DAO\DAOResponse\DAOResponseInterface;
use App\DAO\Filter\QueryFilter;
use App\Entities\Agendamento\Agendamento;
use App\Entities\AgendamentoPet\AgendamentoPet;
use App\Infra\K2web\K2web;
use App\Mappers\AgendamentoMapper;
use App\Models\AgendamentoModel;
use App\Models\AgendamentoPetModel;
use App\Services\JWT\UsuarioLogado;
use App\Services\LogService;
use App\Utils\Collections\QueryFilterCollection;
use App\ViewModels\Agendamento\AgendamentoInfoViewModel\AgendamentoInfoViewModel;
use App\ViewModels\Agendamento\AgendamentoRegistrationViewModel\AgendamentoRegistrationViewModel;
use App\ViewModels\Agendamento\AgendamentoUpdateViewModel\AgendamentoUpdateViewModel;
use Exception;

class AgendamentoDAO extends DAO {

	public function persistAgendamentoRegistrationVM(AgendamentoRegistrationViewModel $agenda_registration_vm): DAOResponse {
        $dao_response = new DAOResponse();
        try {
            $this->db->transStart();
            $agendamento = $this->persistAgendamentoFromAgendamentoRegistrationVM( $agenda_registration_vm );
            $agendamento_pet = $this->persistAgendamentoPetFromAgendamentoRegistrationVM( $agenda_registration_vm, $agendamento );
            $error = $this->db->error();
            if ($error && $error ['code']) {

                $this->setDatabaseErrorsForPetRegistration( $dao_response, $error );
            } else {
                $dao_response->setStatus( DAOResponseInterface::SUCESS );
                $dao_response->setData( $agendamento );
                $this->db->transComplete();
            }
        } catch ( Exception $exception ) {
            $this->setDatabaseErrorsForPetRegistration( $dao_response, $this->db->error() );
        }
		return $dao_response;
	}

    private function persistAgendamentoFromAgendamentoRegistrationVM(AgendamentoRegistrationViewModel &$agenda_registration_vm): Agendamento {

        $agendamento_model = new AgendamentoModel();
        $agendamento = AgendamentoMapper::getAgendamentoFromAgendamentoRegistrationVM( $agenda_registration_vm );
        $agendamento_model->registerAgendamento( $agendamento );
        if (! $agendamento->getId()) {
            throw new Exception( 'O cadastro de agendamento não teve êxito' );
        }
        return $agendamento;
    }

    private function persistAgendamentoPetFromAgendamentoRegistrationVM(AgendamentoRegistrationViewModel &$agenda_registration_vm, Agendamento $agendamento): AgendamentoPet {

        $agendamento_model = new AgendamentoPetModel();
        $agendamento = AgendamentoMapper::getAgendamentoPetFromAgendamentoRegistrationVM( $agenda_registration_vm, $agendamento );
        $agendamento_model->registerAgendamentoPet( $agendamento );
        return $agendamento;
    }

    private function setDatabaseErrorsForPetRegistration(DAOResponse $dao_response, array $error): void {
        if ($error ['code']) {
            $dao_response->setStatus( $error ['code'] );
            $dao_response->setErrors( $this->getDatabaseResponseErrorsForPersistence( $error ) );
        } else {
            $dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
        }
        log_message( 'error', json_encode( $error ) );
    }

    private function getDatabaseResponseErrorsForPersistence(array $error): array {

        $dao_response_errors = [ ];
        if ($error && $error ['code']) {
            if ($error ['code'] === DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS) {
                if (strpos( $error ['message'], 'fk_agendamento_id_agenda' ) !== false) {
                    $dao_response_errors ['id_agenda'] = 'Essa agenda não existe.';
                }
                if (strpos( $error ['message'], 'fk_agendamento_id_cliente' ) !== false) {
                    $dao_response_errors ['id_cliente'] = 'Esse cliente não existe.';
                }
                if (strpos( $error ['message'], 'fk_agendamento_id_paciente' ) !== false) {
                    $dao_response_errors ['id_paciente'] = 'Esse paciente não existe.';
                }
                if (strpos( $error ['message'], 'fk_agendamento_id_procedimento' ) !== false) {
                    $dao_response_errors ['id_procedimento'] = 'Esse procedimento não existe.';
                }
                if (strpos( $error ['message'], 'fk_agendamento_adicionado_por' ) !== false) {
                    $dao_response_errors ['adicionado_por'] = 'Esse usuário não existe.';
                }
            } else if ($error ['code'] === DAOResponseInterface::DUPLICATED_UNIQUE_KEY) {
                if (strpos( $error ['message'], 'agendamento_codigo_unique_key' ) !== false) {
                    $dao_response_errors ['id_agenda'] = 'Esse horário já está ocupado.';
                }
            }
        }
        return $dao_response_errors;

    }


	public function cancelAgendamento(int $id_agendamento): DAOResponse {

		$agendamento_model = new AgendamentoModel();
		$num_rows_affected = $agendamento_model->cancelAgendamento( $id_agendamento );
		// TODO: Validar se agendamento está vinculado a alguma consulta
		$error = $this->db->error();
		if ($error && $error ['code']) {
			(new LogService())->log_error( $error ['code'], "Ocorreu um erro durante o cancelamento do agendamento $id_agendamento", $error );
			return DAOResponse::builder()->setStatus( $error ['code'] )->build();
		}
		return DAOResponse::builder()->setStatus( $num_rows_affected ? DAOResponseInterface::SUCESS : DAOResponseInterface::RECORD_NOT_FOUND )->build();

	}

    public function cancelAgendamentoTelevet(int $id_agendamento): DAOResponse {

        $agendamento_model = new AgendamentoModel();
        $num_rows_affected = $agendamento_model->cancelAgendamentoTelevet( $id_agendamento );
        // TODO: Validar se agendamento está vinculado a alguma consulta
        $error = $this->db->error();
        if ($error && $error ['code']) {
            (new LogService())->log_error( $error ['code'], "Ocorreu um erro durante o cancelamento do agendamento $id_agendamento", $error );
            return DAOResponse::builder()->setStatus( $error ['code'] )->build();
        }
        return DAOResponse::builder()->setStatus( $num_rows_affected ? DAOResponseInterface::SUCESS : DAOResponseInterface::RECORD_NOT_FOUND )->build();

    }

	public function updateAgendamento(int $id_agendamento, AgendamentoUpdateViewModel $agendamento_update_vm): DAOResponse {

		$agendamento_model = new AgendamentoModel();
		$agendamento = AgendamentoMapper::getAgendamentoFromAgendamentoUpdateVM( $agendamento_update_vm );
		$sucess_update = $agendamento_model->updateAgendamento( $id_agendamento, $agendamento );
		$dao_response = new DAOResponse();
		$error = $this->db->error();
		if ($error && $error ['code']) {
			$dao_response->setStatus( $error ['code'] );
			$dao_response_errors = [ ];
			if ($error ['code'] === DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS) {
				if (strpos( $error ['message'], 'fk_agendamento_id_agenda' ) !== false) {
					$dao_response_errors ['id_agenda'] = 'Essa agenda não existe.';
				}
				if (strpos( $error ['message'], 'fk_agendamento_id_cliente' ) !== false) {
					$dao_response_errors ['id_cliente'] = 'Esse cliente não existe.';
				}
				if (strpos( $error ['message'], 'fk_agendamento_id_paciente' ) !== false) {
					$dao_response_errors ['id_paciente'] = 'Esse paciente não existe.';
				}
				if (strpos( $error ['message'], 'fk_agendamento_id_procedimento' ) !== false) {
					$dao_response_errors ['id_procedimento'] = 'Esse procedimento não existe.';
				}
			} else if ($error ['code'] === DAOResponseInterface::DUPLICATED_UNIQUE_KEY) {
				if (strpos( $error ['message'], 'agendamento_codigo_unique_key' ) !== false) {
					$dao_response_errors ['id_agenda'] = 'Esse horário já está ocupado.';
				}
			}
			$dao_response->setErrors( $dao_response_errors );
			(new LogService())->log_error( $error ['code'], "Erro durante a atualização do agendamento $id_agendamento.", [
					'error' => $error,
					'agendamento_update_vm' => $agendamento_update_vm->toArray() ] );
		} else {
			$dao_response->setStatus( $sucess_update ? DAOResponseInterface::SUCESS : DAOResponseInterface::RECORD_NOT_FOUND );
		}
		return $dao_response;

	}

	public function getAgendamentoInfoFromIdAgendamento(int $id_agendamento): DAOResponse {

		$sql = "
            SELECT
               	usuario.id as id_usuario,
                agenda.id_medico as id_agenda,
                paciente.id as id_paciente,
                agendamento.id_cliente as id_cliente,
                consulta.id as id_consulta
            FROM
                agendamento
            INNER JOIN agenda ON agenda.id = agendamento.id_agenda 
            INNER JOIN paciente ON agendamento.id_paciente = paciente.id
            INNER JOIN pessoa ON paciente.id_pessoa = pessoa.id
            LEFT JOIN usuario ON usuario.id_pessoa = pessoa.id
            LEFT JOIN consulta ON consulta.id_agendamento = agendamento.id
            WHERE 
                  agendamento.id = :id_agendamento: AND 
                  (agendamento.cancelado_em = '1900-01-01 00:00:00' OR agendamento.cancelado_em = '0000-00-00 00:00:00')
            LIMIT 1
        ";
		$binds = [
				'id_agendamento' => $id_agendamento ];
		return $this->getDAOResponseFromUniqueRowSelect( $binds, null, $sql, function (array $result_array): AgendamentoInfoViewModel {
			return AgendamentoMapper::getAgendamentoInfoVMFromArray( $result_array [0] );
		} );

	}

    public function getAgendamentoInfoFromIdAgendamentoTelevet(int $id_agendamento): DAOResponse {

        $sql = "
            SELECT
               	usuario.id as id_usuario,
                agenda.id_medico as id_agenda,
                paciente.id as id_paciente,
                agendamento.id_cliente as id_cliente,
                consulta.id as id_consulta
            FROM
                agendamento
            INNER JOIN agenda ON agenda.id = agendamento.id_agenda 
            INNER JOIN paciente ON agendamento.id_paciente = paciente.id
            INNER JOIN pessoa ON paciente.id_pessoa = pessoa.id
            LEFT JOIN usuario ON usuario.id_pessoa = pessoa.id
            LEFT JOIN consulta ON consulta.id_agendamento = agendamento.id
            WHERE 
                  agendamento.id = :id_agendamento: 
            LIMIT 1
        ";
        $binds = [
            'id_agendamento' => $id_agendamento ];
        return $this->getDAOResponseFromUniqueRowSelect( $binds, null, $sql, function (array $result_array): AgendamentoInfoViewModel {
            return AgendamentoMapper::getAgendamentoInfoVMFromArray( $result_array [0] );
        } );

    }

	public function getAgendamento(QueryFilterCollection $query_filter_collection): DAOResponse {

		$sql = '
            SELECT
                paciente.id as id_paciente,
                procedimento.id as id_procedimento,
                agendamento.id as id_agendamento,
                tipo_procedimento.descricao as descricao_tipo_procedimento,
                procedimento.descricao as descricao_procedimento,
                agenda.data as data,
                agenda.hora_inicio as hora_inicio,
                agenda.hora_final as hora_final,
                agenda.codigo as codigo,
                pessoa_fisica.cpf as cpf,
                pessoa_fisica.rg as rg,
                pessoa_fisica.nome as nome,
                pessoa_fisica.genero as genero,
                pessoa_fisica.profissao as profissao,
                pessoa_fisica.estado_civil as estado_civil,
                SUBSTRING(pessoa_fisica.data_nascimento, 1, 10) AS data_nascimento,
                pessoa.telefone_1 as telefone_1,
                pessoa.telefone_2 as telefone_2,
                endereco.cep as cep,
                endereco.numero as numero,
                endereco.complemento as complemento,
                endereco.referencias as referencias,
                cep.estado as estado,
                cep.cidade as cidade,
                cep.bairro as bairro,
                cep.logradouro as logradouro,
                usuario.email as email,
                usuario.foto as foto
            FROM paciente, procedimento, agendamento, agenda, tipo_procedimento, pessoa, profissional, pessoa_fisica,
                 usuario, endereco, cep
            WHERE
                agendamento.id_agenda = agenda.id AND
                paciente.id = agendamento.id_paciente AND
                paciente.id_pessoa = pessoa.id AND
                pessoa.id = pessoa_fisica.id_pessoa AND
                pessoa.id = usuario.id_pessoa AND
                pessoa.id_endereco = endereco.id AND
                endereco.cep = cep.id AND
                agendamento.id_procedimento = procedimento.id AND
                procedimento.id_tipo_procedimento = tipo_procedimento.id
        ';
		$binds = [ ];
		return $this->getDAOResponseFromUniqueRowSelect( $binds, $query_filter_collection, $sql, function (array $result_array) {
			return AgendamentoMapper::getAgendamentoDetailsVMFromArray( $result_array [0] );
		} );

	}

	public function getAgendamentoSala($id_requisicao_procedimento): DAOResponse {
		
		$sql = '
           SELECT
           		id_requisicao_procedimento,
                id_meeting,
                name_meeting,
                description_meeting,
                url_meeting,
                tipo_meeting,
                status_meeting,
                tipoacesso_meeting,
                stream_name_meeting,
                live_stream_id_meeting,
                a.id as id_agendamento
            FROM
                requisicao_sala_video rsv
            LEFT JOIN agendamento a on rsv.id_requisicao_procedimento = a.id_requisicao 
            WHERE
                id_requisicao_procedimento = :id_requisicao_procedimento:
        ';
		$binds = ['id_requisicao_procedimento' => $id_requisicao_procedimento];
		$query = $this->db->query( $sql, $binds );
		$result_array = $query->getResultArray();


        $sql3 = '
            select pf.nome as nome from usuario u  
                inner join pessoa_fisica pf on u.id_pessoa = pf.id_pessoa 
            WHERE
                u.id = :usuario:
        ';
        $binds3 = ['usuario' => UsuarioLogado::getUsuarioId()];
        $query3 = $this->db->query( $sql3, $binds3 );
        $result_usuario = $query3->getResultArray();


        $mensagem = new K2web();
        $mensagem->enviarMensagem(4, $result_array[0]['id_agendamento']);


        $sql2 = '
           UPDATE requisicao_sala_video SET available = 1 where id_requisicao_procedimento = :id_requisicao_procedimento:
        ';
        $binds2 = ['id_requisicao_procedimento' => $id_requisicao_procedimento];
        $query2 = $this->db->query( $sql2, $binds2 );
        $result_array2 = $query2->getResultArray();
		
		if($result_array != null):
		//INTEGRAÇÃO PARA CAPTURAR O LINK DA REUNIÃO
		$curl = curl_init();
		curl_setopt_array($curl, array(
				CURLOPT_URL => 'https://televplay.eadmeeting.com/api/v2/Meetingacesso',
				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 => 'meeting_url='.$result_array[0]['url_meeting'].'&user_nome='.$result_usuario[0]['nome'] .'&user_email='. $result_array[0]['description_meeting'] .'&user_avatar=\'%20\'&user_isadmin=0',
				CURLOPT_HTTPHEADER => array(
						'Authorization: OTRhNWQ3YjY1NzRmZDU0Y2I4OTY4NDRlMjk2NGQ4M2QwYTRlYjM3Yw==',
						'Content-Type: application/x-www-form-urlencoded',
						'Cookie: PHPSESSID=thc4lcitp13cb16c0qduumqod5'
				),
		));
		
		$response = json_decode(curl_exec($curl), true);
		curl_close($curl);
		$dao_response = DAOResponse::builder()->setStatus(DAOResponseInterface::SUCESS)->build();
		$dao_response->setData($response);
		return $dao_response;
		else:
		$dao_response = DAOResponse::builder()->setStatus(DAOResponseInterface::RECORD_NOT_FOUND)->build();
		return $dao_response;
		endif;
	}
	
	
	public function medicoIsLinkedToAgendamento(int $medico_uid, int $id_agendamento): bool {

		$sql = "
            SELECT
                1
            FROM
                agendamento, agenda, medico, profissional, pessoa, usuario
            WHERE 
                agendamento.id_agenda = agenda.id AND
                agenda.id_medico = medico.id AND
                profissional.id_pessoa = pessoa.id AND
                medico.id_profissional = profissional.id AND
                pessoa.id = usuario.id_pessoa AND 
                usuario.id = :uid: AND 
                agendamento.id = :id_agendamento:
            LIMIT 1
        ";
		$binds = [
				'uid' => $medico_uid,
				'id_agendamento' => $id_agendamento ];
		$query = $this->db->query( $sql, $binds );
		$result_array = $query->getResultArray();
		return count( $result_array ?? [ ]) === 1;

	}
	
	public function assistenteIsLinkedToAgendamento(int $assistente_uid, int $id_agendamento): bool {
		
		$sql = "
            SELECT
                1
            FROM
                agendamento, agenda, assistente, profissional, pessoa, usuario
            WHERE
                agendamento.id_agenda = agenda.id AND
                agenda.id_assistente = assistente.id AND
                profissional.id_pessoa = pessoa.id AND
                assistente.id_profissional = profissional.id AND
                pessoa.id = usuario.id_pessoa AND
                usuario.id = :uid: AND
                agendamento.id = :id_agendamento:
            LIMIT 1
        ";
		$binds = [
				'uid' => $assistente_uid,
				'id_agendamento' => $id_agendamento ];
		$query = $this->db->query( $sql, $binds );
		$result_array = $query->getResultArray();
		return count( $result_array ?? [ ]) === 1;
		
	}

}
