<?php

namespace App\Http\Controllers\Admin;

use App\Enums\StatusAgendamentoAtendimentoEnum;
use App\Enums\TipoAgendamentoEnum;
use App\Enums\TipoPrestadorServicoEnum;
use App\Http\Controllers\Controller;
use App\Http\Requests\AgendamentoAtendimentoStoreRequest;
use App\Models\AgendamentoAtendimento;
use App\Models\Dependente;
use App\Models\Feriado;
use App\Models\Funcionario;
use App\Models\MotivoCancelamentoAgendamento;
use App\Models\PrestadorServico;
use App\Models\ServicoAtendimento;
use App\Models\Servidor;
use App\Services\AgendaAtendimentoJuridicoService;
use App\Services\AgendaAtendimentoOdontologicoService;
use App\Services\CancelaAgendamentoAtendimentoService;
use Carbon\Carbon;
use Illuminate\Http\Request;

class AgendamentoAtendimentoController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        $this->validate($request, [
            'tipo' => 'required|in:ODONTOLOGICO,JURIDICO'
        ]);
        
        $tipo = TipoAgendamentoEnum::make($request->tipo);
        $getTipoPrestador = $this->getTipoPrestador($tipo);
        $prestadores = PrestadorServico::deTipo($getTipoPrestador)
                                        ->comHorarioDefinido()
                                        ->get();
        $feriados = Feriado::all();
        return view('admin.agendamentos-atendimentos.index', [
            'prestadores' => $prestadores,
            'tipo' => $tipo,
            'feriados' => $feriados,
        ]);
    }

    public function busca(Request $request)
    {
        $this->validate($request, [
            'tipo' => 'required|in:ODONTOLOGICO,JURIDICO',
        ]);

        $tipo = TipoAgendamentoEnum::make($request->tipo);
        if ($request->tipo_pessoa && $request->pessoa) {
            $pessoa = $this->buscaPessoaPorTipoId($request->tipo_pessoa, $request->pessoa);
            $agendamentos = AgendamentoAtendimento::deTipo($tipo)->daPessoa($pessoa)->orderBy('inicio', 'desc')->get();
        } else {
            $agendamentos = [];
        }
        return view('admin.agendamentos-atendimentos.busca', [
            'tipo' => $tipo,
            'agendamentos' => $agendamentos,
        ]);
    }

    private function getTipoPrestador(TipoAgendamentoEnum $tipo)
    {
        if ($tipo->isOdontologico()) {
            return TipoPrestadorServicoEnum::dentista();
        }
        
        return TipoPrestadorServicoEnum::advogado();
    }

    public function load(Request $request)
    {
        $tipo = TipoAgendamentoEnum::make($request->tipo);
        $inicio = $request->has('start') ? Carbon::make($request->start) : null;
        $fim = $request->has('end') ? Carbon::make($request->end) : null;

        if ($tipo->isOdontologico()) {
            $agendamentos =  AgendamentoAtendimento::deTipo($tipo)
                                                    ->whereBetween('inicio', [$inicio, $fim]);
            if ($request->prestador) {
                $prestador = PrestadorServico::find($request->prestador);
                $agendamentos = $agendamentos->doPrestador($prestador);
            }
        } else {
            $ocultarFaltososDesmarcados = $request->boolean('ocultarFaltososDesmarcados');
            $servico = ServicoAtendimento::juridico();
            $agendamentos =  AgendamentoAtendimento::deTipo($tipo)
                                                    ->when($ocultarFaltososDesmarcados, function($query) {
                                                        $query->emAbertoOuEsperandoRecepcao();
                                                    })
                                                    ->doServicoAtendimento($servico)
                                                    ->whereBetween('inicio', [$inicio, $fim]);
        }

        return $agendamentos->get()->map(function ($agendamento) use ($tipo) {
            $backgroundColor = '';
            if ($agendamento->status->isAtendido()) {
                $backgroundColor = '#2fbd5c';
            }

            if ($agendamento->status->isFaltou()) {
                $backgroundColor = '#d64022';
            }

            if ($agendamento->status->isDesmarcou()) {
                $backgroundColor = '#adadad';
            }

            if ($agendamento->status->isEsperandoRecepcao()) {
                $backgroundColor = '#f7d152';
            }

            return [
                'title' => $agendamento->agendavel->nome,
                'start' => $agendamento->inicio->toDateTimeString(),
                'end' => $agendamento->fim->toDateTimeString(),
                'url' => route('admin.agendamentos-atendimentos.show', [
                    'agendamento' => $agendamento->id,
                ]),
                'backgroundColor' => $backgroundColor
            ];
        });
    }

    /**
     * Responde se data está indisponível, para desativar a data no calendário
     */
    public function obtemDataIndisponivel(Request $request)
    {
        $data = Carbon::createFromFormat('d/m/Y', $request->data);
        $indisponivel = Feriado::eFeriado($data);
        return response()->json(['indisponivel' => $indisponivel]);
    }

    public function buscaPessoa(Request $request)
    {
        $termo = $request->q;
        $tipo = $request->tipo;

        if ($tipo == 'funcionario') {
            $results = Funcionario::where('nome', 'ilike', "%{$termo}%")
                            ->orWhere('cpf', $termo)->get();
        }

        if ($tipo == 'associado') {
            $results = Servidor::associado()->buscaPorTermo($termo)->get();
        }
        
        if ($tipo == 'dependente') {
            $results = Dependente::buscaPorTermo($termo)->get();
        }

        return $results->map(function ($pessoa) {
            return [
                'text' => $pessoa->nome,
                'id' => $pessoa->id,
            ];
        });
    }

    public function buscaPessoaPorTipoId($tipo, $id)
    {
        if ($tipo == 'funcionario') {
            $pessoa = Funcionario::findOrFail($id);
        }

        if ($tipo == 'associado') {
            $pessoa = Servidor::associado()->findOrFail($id);
        }

        if ($tipo == 'dependente') {
            $pessoa = Dependente::findOrFail($id);
        }

        return $pessoa;
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create(Request $request)
    {
        $this->validate($request, [
            'tipo' => 'required|in:ODONTOLOGICO,JURIDICO'
        ]);
        
        $tipo = TipoAgendamentoEnum::make($request->tipo);
        $prestadores = PrestadorServico::deTipo($this->getTipoPrestador($tipo))
                                        ->comHorarioDefinido()
                                        ->get();
        $feriados = Feriado::all();
        return view('admin.agendamentos-atendimentos.create', [
            'data' => $request->data,
            'hora' => $request->hora,
            'prestadores' => $prestadores,
            'prestador_id' => $request->prestador,
            'tipo' => $tipo,
            'feriados' => $feriados
        ]);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(AgendamentoAtendimentoStoreRequest $request)
    {
        $tipoAgendamento = TipoAgendamentoEnum::make($request->tipo_agendamento);
        $pessoa = $this->buscaPessoaPorTipoId($request->tipo, $request->pessoa);
        $prestador = PrestadorServico::find($request->prestador_servico_id);

        $inicio = Carbon::createFromFormat('d/m/Y G:i', $request->hora_inicio);
        $fim = Carbon::createFromFormat('d/m/Y G:i', $request->hora_fim);
        $emergencia = $request->boolean('emergencia', false);
        $emTratamento = $request->boolean('em_tratamento', false);
        $extraordinario = $request->boolean('extraordinario', false);
        $permiteAgendamentoProximo = $request->boolean('permiteAgendamentoProximo', false);
        $motivoExtraordinario = $request->motivo_extraordinario;

        if ($tipoAgendamento->isOdontologico()) {
            $agendamentoService = new AgendaAtendimentoOdontologicoService($inicio, $fim, $prestador, $pessoa, $emergencia, $emTratamento);
        } else {
            $agendamentoService = new AgendaAtendimentoJuridicoService($inicio, $fim, $pessoa, ServicoAtendimento::juridico(), $extraordinario, $motivoExtraordinario, $permiteAgendamentoProximo);
        }

        $agendamento = $agendamentoService->agenda();

        return redirect()
                ->route('admin.agendamentos-atendimentos.index', ['tipo' => $request->tipo_agendamento])
                ->with('pessoaSemAnamnese', !$pessoa->anamnese->exists && $tipoAgendamento->isOdontologico())
                ->with('agendamento', $agendamento->id)
                ->with('pessoa', $request->pessoa)
                ->with('tipo', $request->tipo)
                ->with('status', 'Agendamento incluído com sucesso!');
    }

    public function show(AgendamentoAtendimento $agendamento)
    {
        if ($agendamento->tipo->isOdontologico()) {
            $prestadores = PrestadorServico::deTipo(TipoPrestadorServicoEnum::dentista())
                                            ->comHorarioDefinido()
                                            ->get();
        }
        $statuses = StatusAgendamentoAtendimentoEnum::getAll();
        $tipoAgendavel = $this->getTipoAgendavel($agendamento);

        return view('admin.agendamentos-atendimentos.show', [
            'agendamento' => $agendamento,
            'prestadores' => $prestadores ?? null,
            'statuses' => $statuses,
            'tipoAgendamento' => $agendamento->tipo,
            'motivosCancelamento' => MotivoCancelamentoAgendamento::orderBy('descricao')->get(),
            'tipo' => $tipoAgendavel
        ]);
    }

    private function getTipoAgendavel($agendamento)
    {
        if ($agendamento->agendavel instanceof Dependente) {
            return 'dependente';
        }

        if ($agendamento->agendavel instanceof Servidor) {
            return 'associado';
        }

        if ($agendamento->agendavel instanceof Funcionario) {
            return 'funcionario';
        }
    }

    public function desmarcar(Request $request, AgendamentoAtendimento $agendamento)
    {
        $this->validate($request, [
            'motivo' => 'required',
            'observacao' => 'nullable|max:150'
        ]);

        $motivo = MotivoCancelamentoAgendamento::find($request->motivo);
        $service = new CancelaAgendamentoAtendimentoService($agendamento, $motivo, $request->observacao);
        $service->cancela();

        return redirect()
            ->route('admin.agendamentos-atendimentos.index', ['tipo' => $agendamento->tipo->getName()])
            ->with('status', 'Agendamento desmarcado com sucesso!');
    }

    public function faltou(Request $request, AgendamentoAtendimento $agendamento)
    {
        $agendamento->status = StatusAgendamentoAtendimentoEnum::faltou();
        $agendamento->save();

        return redirect()
            ->route('admin.agendamentos-atendimentos.index', ['tipo' => $agendamento->tipo->getName()])
            ->with('status', 'Agendamento atualizado com sucesso!');
    }

    public function esperando(Request $request, AgendamentoAtendimento $agendamento)
    {
        $agendamento->status = StatusAgendamentoAtendimentoEnum::esperandoRecepcao();
        $agendamento->save();

        return redirect()
            ->route('admin.agendamentos-atendimentos.index', ['tipo' => $agendamento->tipo->getName()])
            ->with('status', 'Agendamento atualizado com sucesso!');
    }
}
