<?php

namespace App\Services;

use App\DAO\AgendaMedicoDAO;
use App\DAO\AgendaMedicoDisponibilidadeDAO;
use App\DAO\DAOResponse\DAOResponseInterface;
use App\DAO\Pagination\Pagination;
use App\Entities\AgendaMedico\AgendaMedico;
use App\Mappers\AgendaMedicoDisponibilidadeMapper;
use App\Mappers\AgendaMedicoMapper;
use App\Services\ServiceResponse\ServiceResponse;
use App\Services\ServiceResponse\ServiceResponseInterface;
use App\Utils\Collections\GeneralCollection;
use App\Utils\Collections\QueryFilterCollection;
use App\Utils\Collections\QueryOrderCollection;
use App\Utils\DateUtils;
use App\Utils\ResponseException;
use App\ViewModels\AgendaMedico\AgendaMedicoMultipleRegistrationViewModel\AgendaMedicoMultipleRegistrationViewModel;
use App\ViewModels\AgendaMedico\AgendaMedicoRegistrationViewModel\AgendaMedicoRegistrationViewModel;

class AgendaMedicoService extends Service
{

    public function registerAgendaMedico(array $agenda_array): ServiceResponse
    {

        try {
            $agenda_registration_vm = AgendaMedicoMapper::getAgendaMedicoRegistrationVMFromArray($agenda_array);
        } catch (ResponseException $exception) {
            return ServiceResponse::builder()->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR)
                ->setErrors($exception->getData())
                ->build();
        }
        $agenda_dao = new AgendaMedicoDAO();
        $schedules_conflicts = $this->getSchedulesConflicts($agenda_dao, $agenda_registration_vm);
        if ($schedules_conflicts->length > 0) {
            $service_response = ServiceResponse::builder()->setStatus(ServiceResponseInterface::HAVE_CONFLICTS)
                ->setErrors([
                    'conflitos' => $schedules_conflicts->toArray()
                ])
                ->build();
        } else {
            $dao_response = $agenda_dao->persistAgendaMedicoRegistrationVM($agenda_registration_vm);
            $service_response = new ServiceResponse();
            switch ($dao_response->getStatus()) {
                case DAOResponseInterface::SUCESS :
                    $service_response->setStatus(ServiceResponseInterface::SUCESS);
                    $service_response->setData($this->getRegisterAgendaMedicoDataResponse($dao_response->getData()));
                    break;
                case DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS :
                    $service_response->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR);
                    $service_response->setErrors($dao_response->getErrors());
                    break;
                case DAOResponseInterface::DUPLICATED_UNIQUE_KEY :
                    $service_response->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR);
                    $service_response->setErrors($dao_response->getErrors());
                    break;
                default :
                    $service_response->setStatus(ServiceResponseInterface::DATABASE_CONNECTION_ERROR);
                    break;
            }
        }
        return $service_response;

    }

    public function registerAgendaMedicoDisponibilidade($request): ServiceResponse
    {

        $agenda_reg_base = AgendaMedicoDisponibilidadeMapper::getAgendaMedicoRegistrationVMFromArray($request);

        $dao = new AgendaMedicoDisponibilidadeDAO();
        $agenda_datas_response = $dao->getDatasToRegister($agenda_reg_base);

        if ($agenda_datas_response->getStatus() == DAOResponseInterface::SUCESS) {
            $resultado = $agenda_datas_response->getData();
            $lista = [];
            for ($i = 0; $i < count($resultado); $i++) {
                $item = $resultado [$i];
                $obj = AgendaMedicoDisponibilidadeMapper::getAgendaMedicoRegistrationVMFromArray($request);
                $crc = $obj->getIdUnidade() . "-" . $obj->getIdMedico() . "-" . $item ['data'];
                $obj->setCrc($crc);
                $obj->setDataDisponibilidade($item ['data']);
                array_push($lista, $obj);
            }

            $daoResponse = $dao->registerAgendaMedicoDisponibilidade($lista);
            $service_response = new ServiceResponse();
            $service_response = $this->setServiceResponse($daoResponse);
        }

        return $service_response;

    }

    public function getAgendaMedicoDisponibilidade(int $idUnidade, int $idMedico, int $dataInicial, int $dataFinal): ServiceResponse
    {

        if ($dataInicial > $dataFinal) {
            $temp = $dataFinal;
            $dataFinal = $dataInicial;
            $dataInicial = $temp;
        }

        $dt_ini = DateUtils::requestInt_to_date($dataInicial);
        $dt_fim = DateUtils::requestInt_to_date($dataFinal);

        $service_response = new ServiceResponse();
        $agenda_dao = new AgendaMedicoDisponibilidadeDAO();
        $daoResponse = $agenda_dao->getAgendaMedicoDisponibilidade($idUnidade, $idMedico, $dt_ini, $dt_fim);
        $service_response = $this->setServiceResponse($daoResponse);
        return $service_response;

    }

    public function updateAgendaMedicoDisponibilidade(array $request): ServiceResponse
    {

        $idAgenda = $request ['id_agenda'];
        $ativo = $request ['ativo'];

        $service_response = new ServiceResponse();
        $agenda_dao = new AgendaMedicoDisponibilidadeDAO();
        $daoResponse = $agenda_dao->updateAgendaMedicoDisponibilidade($idAgenda, $ativo);
        $service_response = $this->setServiceResponse($daoResponse);
        return $service_response;

    }

    public function deleteAgendaMedico(int $id_agenda)
    {

        $agenda_dao = new AgendaMedicoDAO();
        $dao_response = $agenda_dao->deleteAgendaMedico($id_agenda);
        $service_response_status = [
            DAOResponseInterface::RESTRICT_FK_UPDATE_OR_DELETION => ServiceResponseInterface::RECORD_IS_LINKED,
            DAOResponseInterface::SUCESS => ServiceResponseInterface::SUCESS,
            DAOResponseInterface::RECORD_NOT_FOUND => ServiceResponseInterface::RECORD_NOT_FOUND
        ] [$dao_response->getStatus()];
        $service_response = ServiceResponse::builder()->setStatus($service_response_status)
            ->build();
        return $service_response;

    }

    private function getRegisterAgendaMedicoDataResponse(AgendaMedico $agenda): array
    {

        return [
            'id_agenda' => $agenda->getId()
        ];

    }

    private function getSchedulesConflicts(AgendaMedicoDAO $agenda_dao, AgendaMedicoRegistrationViewModel $agenda_registration_vm): GeneralCollection
    {

        $dao_response = $agenda_dao->getAgendaMedicoConflicts($agenda_registration_vm);
        if ($dao_response->getStatus() === DAOResponseInterface::SUCESS) {
            return $dao_response->getData();
        } else {
            return new GeneralCollection();
        }

    }

    public function getAgendaMedicoListByMedicoID(QueryFilterCollection $query_filter_collection, ?Pagination $pagination,
                                                         QueryOrderCollection  $query_order_collection): ServiceResponse
    {

        $agenda_dao = new AgendaMedicoDAO();
        // $query_filter_collection->push(QueryFilter::builder()
        // ->setTable('agenda')
        // ->setNameParam('id_MEDICO')
        // ->setValue($medico_id)
        // ->setIsExact(true)
        // ->build()
        // );
        $dao_response = $agenda_dao->getAgendaMedicoList($query_filter_collection, $query_order_collection, $pagination);
        switch ($dao_response->getStatus()) {
            case DAOResponseInterface::SUCESS :
                $service_response_status = ServiceResponseInterface::SUCESS;
                break;
            case DAOResponseInterface::RECORD_NOT_FOUND :
                $service_response_status = ServiceResponseInterface::RECORD_NOT_FOUND;
                break;
            default :
                $service_response_status = ServiceResponseInterface::DATABASE_CONNECTION_ERROR;
                break;
        }
        $service_response = ServiceResponse::builder()->setStatus($service_response_status)
            ->build();
        if ($service_response_status === DAOResponseInterface::SUCESS) {
            $dao_response_data = $dao_response->getData();
            $agenda_details_array = AgendaMedicoMapper::getAgendaMedicoListingArrayFromAgendaMedicoListingVMCollection($dao_response_data);
            $pagination = $dao_response->getPagination();
            $data = [
                'agendas' => $agenda_details_array,
                'pagination' => $pagination ? $pagination->toResponseArray() : null
            ];
            $service_response->setData($data);
        }
        return $service_response;

    }

    public function registerMultipleAgendaMedico(array $agenda_array): ServiceResponse
    {

        try {
            $agenda_multiple_registration_vm = AgendaMedicoMapper::getAgendaMedicoMultipleRegistrationVMFromArray($agenda_array);
        } catch (ResponseException $exception) {
            return ServiceResponse::builder()->setErrors($exception->getData())
                ->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR)
                ->build();
        }
        $daily_schedules_collection = AgendaMedicoMapper::getDailySchedulesByAgendaMedicoMultipleRegistrationVM($agenda_multiple_registration_vm);
        $service_dates_collection = AgendaMedicoMapper::getServiceDatesCollectionByAgendaMedicoMultipleRegistrationVM(
            $agenda_multiple_registration_vm);
        $agenda_dao = new AgendaMedicoDAO();
        $potential_conflicts = $this->getMultipleRegisterAgendaMedicoConflictsCollection($agenda_dao, $agenda_multiple_registration_vm,
            $service_dates_collection);
        $conflicts = new GeneralCollection();
        $agenda_collection = AgendaMedicoMapper::getAgendaMedicoCollectionBySchedulesAndDates($agenda_multiple_registration_vm,
            $daily_schedules_collection, $service_dates_collection, $potential_conflicts, $conflicts);
        if ($agenda_collection->length > 0) {
            $dao_response = $agenda_dao->persistAgendaMedicoCollection($agenda_collection, $agenda_multiple_registration_vm->getEncaixe());
            $dao_response_status = $dao_response->getStatus();
            $service_response = new ServiceResponse();
            if (in_array($dao_response_status, [
                DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS,
                DAOResponseInterface::DUPLICATED_UNIQUE_KEY
            ])) {
                $service_response->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR);
                $service_response->setErrors($dao_response->getErrors());
            } else if ($dao_response_status === DAOResponseInterface::SUCESS) {
                if ($conflicts->length) {
                    $service_response->setStatus(ServiceResponseInterface::PARTIAL_SUCESS);
                    $service_response->setErrors([
                        'conflitos' => $conflicts->toArray()
                    ]);
                } else {
                    $service_response->setStatus(ServiceResponseInterface::SUCESS);
                }
            } else {
                $service_response->setStatus(ServiceResponseInterface::DATABASE_CONNECTION_ERROR);
            }
            return $service_response;
        } else {
            return ServiceResponse::builder()->setStatus(ServiceResponseInterface::HAVE_CONFLICTS)
                ->setErrors([
                    'conflitos' => $conflicts->toArray()
                ])
                ->build();
        }

    }

    private function getMultipleRegisterAgendaMedicoConflictsCollection(AgendaMedicoDAO                           $agenda_dao,
                                                                               AgendaMedicoMultipleRegistrationViewModel $agenda_multiple_registration_vm, GeneralCollection $service_dates_collection): GeneralCollection
    {

        $dao_response = $agenda_dao->getAgendaMedicoConflictsForMultipleRegistration($agenda_multiple_registration_vm, $service_dates_collection);
        return $dao_response->getStatus() === DAOResponseInterface::SUCESS ? $dao_response->getData() : new GeneralCollection();

    }

    public function updateAgendaMedico(int $id_agenda, array $data): ServiceResponse
    {

        $agenda_update_vm = AgendaMedicoMapper::getAgendaMedicoUpdateVMFromArray($data);
        $agenda_dao = new AgendaMedicoDAO();
        $dao_response = $agenda_dao->updateAgendaMedico($id_agenda, $agenda_update_vm);
        $service_response = new ServiceResponse();
        $response_status = [
            DAOResponseInterface::SUCESS => ServiceResponseInterface::SUCESS,
            DAOResponseInterface::RECORD_NOT_FOUND => ServiceResponseInterface::RECORD_NOT_FOUND,
            DAOResponseInterface::DATABASE_CONNECTION_ERROR => ServiceResponseInterface::DATABASE_CONNECTION_ERROR,
            DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS => ServiceResponseInterface::VALIDATION_PARAMS_ERROR
        ] [$dao_response->getStatus()];
        if (!$response_status) {
            (new LogService())->log_error($response_status, "Ocorreu um erro desconhecido durante a atualização da agenda", []);
            $response_status = ServiceResponseInterface::DATABASE_CONNECTION_ERROR;
        } else if ($response_status !== ServiceResponseInterface::SUCESS) {
            $service_response->setErrors($dao_response->getErrors());
        }
        $service_response->setStatus($response_status);
        return $service_response;

    }

    public function getAgendaUnidadeByMedico(int $especialidade, int $unidade, int $data_inical, int $data_final, Pagination $pagination): ServiceResponse
    {

        if ($data_inical <= $data_final) {
            $data_inicio = DateUtils::requestInt_to_date($data_inical);
            $data_fim = DateUtils::requestInt_to_date($data_final);
        } else {
            $data_inicio = DateUtils::requestInt_to_date($data_final);
            $data_fim = DateUtils::requestInt_to_date($data_inical);
        }

        $dao = new AgendaMedicoDAO();

        $dao_response = $dao->getAgendaUnidadeByMedico($especialidade, $unidade, $data_inicio, $data_fim, $pagination);
        $service_response = new ServiceResponse();
        switch ($dao_response->getStatus()) {
            case DAOResponseInterface::SUCESS :
                $service_response->setStatus(ServiceResponseInterface::SUCESS);
                $service_response->setData($dao_response->getData());
                break;
            case DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS :
                $service_response->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR);
                $service_response->setErrors($dao_response->getErrors());
                break;
            case DAOResponseInterface::DUPLICATED_UNIQUE_KEY :
                $service_response->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR);
                $service_response->setErrors($dao_response->getErrors());
                break;
            default :
                $service_response->setStatus(ServiceResponseInterface::DATABASE_CONNECTION_ERROR);
                break;
        }

        return $service_response;

    }

    public function getAgendaMedicoByUnidadeByPeriodo(int $unidade, int $especialidade, string $data): ServiceResponse
    {

        $dao = new AgendaMedicoDAO();
        $dao_response = $dao->getAgendaMedicoByUnidadeByPeriodo($unidade, $especialidade, $data);
        $service_response = new ServiceResponse();
        switch ($dao_response->getStatus()) {
            case DAOResponseInterface::SUCESS :
                $service_response->setStatus(ServiceResponseInterface::SUCESS);
                $service_response->setData($dao_response->getData());
                break;
            case DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS :
                $service_response->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR);
                $service_response->setErrors($dao_response->getErrors());
                break;
            case DAOResponseInterface::DUPLICATED_UNIQUE_KEY :
                $service_response->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR);
                $service_response->setErrors($dao_response->getErrors());
                break;
            default :
                $service_response->setStatus(ServiceResponseInterface::DATABASE_CONNECTION_ERROR);
                break;
        }

        return $service_response;

    }

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

        $dao = new AgendaMedicoDAO();
        $consultaName = 'Consulta-' . $this->generateRandomString(10);
        $dao_response = $dao->registerNewAgendamentoMedicoByRecepcao($idAgenda, $idProcedimento, $idPet, $idStatus, $idObservacao, $consultaName, $whatsapp);
        $service_response = new ServiceResponse();

        switch ($dao_response->getStatus()) {
            case DAOResponseInterface::SUCESS :
                $service_response->setStatus(ServiceResponseInterface::SUCESS);
                $service_response->setData($dao_response->getData());
                break;
            case DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS :
                $service_response->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR);
                $service_response->setErrors($dao_response->getErrors());
                break;
            case DAOResponseInterface::DUPLICATED_UNIQUE_KEY :
                $service_response->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR);
                $service_response->setErrors($dao_response->getErrors());
                break;
            case DAOResponseInterface::UNIQUE_KEY_VIOLATED :
                $service_response->setStatus(ServiceResponseInterface::HAVE_CONFLICTS);
                $service_response->setErrors($dao_response->getErrors());
                break;
            default :
                $service_response->setStatus(ServiceResponseInterface::DATABASE_CONNECTION_ERROR);
                break;
        }

        return $service_response;

    }

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

        $dao = new AgendaMedicoDAO();
        $dao_response = $dao->updateStatusAgendamentoMedico($idAgenda, $idStatus, $observacao);
        $service_response = new ServiceResponse();

        switch ($dao_response->getStatus()) {
            case DAOResponseInterface::SUCESS :
                $service_response->setStatus(ServiceResponseInterface::SUCESS);
                break;
            case DAOResponseInterface::RECORD_NOT_FOUND :
                $service_response->setStatus(ServiceResponseInterface::RECORD_NOT_FOUND);
                break;
            case DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS :
                $service_response->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR);
                $service_response->setErrors($dao_response->getErrors());
                break;
            case DAOResponseInterface::DUPLICATED_UNIQUE_KEY :
                $service_response->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR);
                $service_response->setErrors($dao_response->getErrors());
                break;
            default :
                $service_response->setStatus(ServiceResponseInterface::DATABASE_CONNECTION_ERROR);
                break;
        }

        return $service_response;

    }


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

        $dao = new AgendaMedicoDAO();
        $dao_response = $dao->reagendarAgendamentoMedicoByRecepcao($idAgendaAntiga, $idAgendaNova);
        $service_response = new ServiceResponse();

        switch ($dao_response->getStatus()) {
            case DAOResponseInterface::SUCESS :
                $service_response->setStatus(ServiceResponseInterface::SUCESS);
                $service_response->setData($dao_response->getData());
                break;
            case DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS :
                $service_response->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR);
                $service_response->setErrors($dao_response->getErrors());
                break;
            case DAOResponseInterface::DUPLICATED_UNIQUE_KEY :
                $service_response->setStatus(ServiceResponseInterface::VALIDATION_PARAMS_ERROR);
                $service_response->setErrors($dao_response->getErrors());
                break;
            default :
                $service_response->setStatus(ServiceResponseInterface::DATABASE_CONNECTION_ERROR);
                break;
        }

        return $service_response;

    }

    public function getAgendaByDate(?Pagination $pagination, ?QueryFilterCollection $query_filter_collection,
                                    ?QueryOrderCollection $query_order_collection, $id_procedimento, $data_agenda, $id_unidade): ServiceResponse
    {
        $agenda_dao = new AgendaMedicoDAO();
        $dao_response = $agenda_dao->getAgendaDateList($pagination, $query_filter_collection, $query_order_collection, $id_procedimento, $data_agenda, $id_unidade);
        switch ($dao_response->getStatus()) {
            case DAOResponseInterface::SUCESS:
                $service_response_status = ServiceResponseInterface::SUCESS;
                break;
            case DAOResponseInterface::RECORD_NOT_FOUND:
                $service_response_status = ServiceResponseInterface::RECORD_NOT_FOUND;
                break;
            case DAOResponseInterface::DATABASE_CONNECTION_ERROR:
                $service_response_status = ServiceResponseInterface::DATABASE_CONNECTION_ERROR;
                break;
        }
        $service_response = ServiceResponse::builder()
            ->setStatus($service_response_status)
            ->build();
        if ($service_response_status === DAOResponseInterface::SUCESS) {
            $dao_response_data = $dao_response->getData();
            $agenda_details_array = AgendaMedicoMapper::getAgendaDateArrayFromAgendaVMCollection($dao_response_data);
            $pagination = $dao_response->getPagination();
            $data = [
                'agendas' => $agenda_details_array,
                'pagination' => $pagination ? $pagination->toResponseArray() : null
            ];
            $service_response->setData($data);
        }
        return $service_response;
    }
}
