<?php

namespace App\Services;

use App\Enums\CategoriaConfiguracaoEnum;
use App\Enums\DiaSemanaEnum;
use App\Enums\StatusAgendamentoAtendimentoEnum;
use App\Enums\TipoAgendamentoEnum;
use App\Exceptions\UltimoAtendimentoMuitoProximoException;
use App\Models\AgendamentoAtendimento;
use App\Models\Configuracao;
use App\Models\Dependente;
use Carbon\Carbon;

class AgendaAtendimentoService
{
    protected $inicio;
    protected $fim;
    protected $tipo;
    protected $emergencia;
    protected $emTratamento;

    public function __construct(Carbon $inicio, Carbon $fim, $pessoa, TipoAgendamentoEnum $tipo)
    {
        $this->inicio = $inicio;
        $this->fim = $fim;
        $this->pessoa = $pessoa;
        $this->tipo = $tipo;
    }

    public function sobrepoeHorario()
    {
        return $this->agendamentoSobreposto() != null;
    }

    public function agendamentoSobreposto()
    {
        return AgendamentoAtendimento::deTipo($this->tipo)
            ->comStatus(StatusAgendamentoAtendimentoEnum::aberto())
            ->where(function ($query) {
                return $query->where(function ($query) {
                    // Horário entre início e fim
                    return $query->where('inicio', '>=', $this->inicio)
                        ->where('fim', '<=', $this->fim);
                })
                    ->orWhere(function ($query) {
                        // Horário engloba (maior) início e fim
                        return $query->where('inicio', '<=', $this->inicio)
                            ->where('fim', '>=', $this->fim);
                    })
                    ->orWhere(function ($query) {
                        return $query->where('fim', '>', $this->inicio)
                            ->where('inicio', '<', $this->fim);
                    })
                    ->orWhere(function ($query) {
                        return $query->where('inicio', '>', $this->inicio)
                            ->where('inicio', '<', $this->fim);
                    });
            })
            ->first();
    }

    public function diaSemana(Carbon $data)
    {
        switch ($data->dayOfWeek) {
            case Carbon::MONDAY:
                return DiaSemanaEnum::SEGUNDA();
            case Carbon::TUESDAY:
                return DiaSemanaEnum::TERCA();
            case Carbon::WEDNESDAY:
                return DiaSemanaEnum::QUARTA();
            case Carbon::THURSDAY:
                return DiaSemanaEnum::QUINTA();
            case Carbon::FRIDAY:
                return DiaSemanaEnum::SEXTA();
            case Carbon::SATURDAY:
                return DiaSemanaEnum::SABADO();
            default:
                return DiaSemanaEnum::DOMINGO();
        }
    }

    protected function verificaSeHaAgendamentoProximo()
    {
        $this->verificaSeHaAgendamentoProximoPessoa();
        $this->verificaSeHaAgendamentoProximoFamilia();
    }

    private function verificaSeHaAgendamentoProximoPessoa()
    {
        $ultimoAgendamento = $this->pessoa->agendamentos()
            ->deTipo($this->tipo)
            ->latest()
            ->first();

        if (
            !empty($ultimoAgendamento) &&
            $this->eAMenosQueQuantidadeMinimaDeDiasDo($ultimoAgendamento) &&
            $ultimoAgendamento->naoDesmarcou() &&
            $this->permiteAgendamentoProximo == false
        ) {
            throw new UltimoAtendimentoMuitoProximoException($this->pessoa->nome, $ultimoAgendamento);
        }
    }

    private function verificaSeHaAgendamentoProximoFamilia()
    {
        if ($this->pessoa instanceof Dependente) {
            $titular = $this->pessoa->dependivel;
        } else {
            $titular = $this->pessoa;
        }

        $outrosDependentes = $titular->dependentes;

        $familia = collect([$titular])->push($outrosDependentes)->flatten();
        $familia->each(function ($familiar) {
            $ultimoAgendamentoFamiliar = $familiar->agendamentos()
                ->deTipo($this->tipo)
                ->naoDesmarcado()
                ->orderBy('inicio', 'desc')
                ->first();

            if (
                $ultimoAgendamentoFamiliar &&
                $this->eAMenosQueQuantidadeMinimaDeDiasDo($ultimoAgendamentoFamiliar) &&
                $this->permiteAgendamentoProximo == false
            ) {
                throw new UltimoAtendimentoMuitoProximoException($familiar->nome, $ultimoAgendamentoFamiliar);
            }
        });
    }

    public function eAMenosQueQuantidadeMinimaDeDiasDo(AgendamentoAtendimento $ultimoAgendamento)
    {
        $configuracao = 'DIAS_ENTRE_AGENDAMENTOS';
        if ($this->tipo->isJuridico()) {
            $quantidadeMinimaDeDias = intval(Configuracao::get($configuracao, CategoriaConfiguracaoEnum::agendamentoJuridico()));
        } else {
            $quantidadeMinimaDeDias = intval(Configuracao::get($configuracao, CategoriaConfiguracaoEnum::agendamentoOdontologico()));
        }

        if ($quantidadeMinimaDeDias == 0) {
            return false;
        }

        return $this->inicio->diffInDays($ultimoAgendamento->inicio) < $quantidadeMinimaDeDias;
    }
}
