<?php

namespace Ignite\Core\Http\Controllers\Admin;

use Exception;
use Ignite\Core\Contracts\Repositories\ParticipantRepository;
use Ignite\Core\Entities\User;
use Ignite\Core\Http\Controllers\Controller;
use Ignite\Core\Http\Forms\ProfileForm;
use Ignite\Core\Models\Grid\ParticipantTable;
use Ignite\Packages\Presenter\Exceptions\PresenterException;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
use Kris\LaravelFormBuilder\FormBuilderTrait;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;

class ParticipantController extends Controller
{
    use FormBuilderTrait;

    /** @var ParticipantRepository */
    protected $participantRepository;

    /**
     * Create a new controller instance.
     *
     * @param ParticipantRepository $participantRepository
     */
    public function __construct(ParticipantRepository $participantRepository)
    {
        $this->participantRepository = $participantRepository;
    }

    /**
     * Show the admin index page which will list participants by status.
     *
     * @param ParticipantTable $table
     * @param Request          $request
     * @return JsonResponse|View|BinaryFileResponse|StreamedResponse
     */
    public function index(ParticipantTable $table, Request $request): JsonResponse|View|BinaryFileResponse|StreamedResponse
    {
        $status = $request->query('status', 'active');
        $statuses = $this->participantRepository->getStatuses();
        $statusLookup = array_flip($statuses);

        if (! array_key_exists($status, $statusLookup)) {
            $status = 'active';
        }

        $current = $status;
        session()->put('participantListType', $current);

        $table->with('status', $statusLookup[$status]);

        if ($request->wantsJson()) {
            return $table->ajax();
        }

        return $table->render('Core::admin.participants.index', compact('statuses', 'current'));
    }

    /**
     * Create a participant.
     *
     * @return View
     */
    public function create()
    {
        $form = $this->form(ProfileForm::class, [
            'method' => 'POST',
            'route' => 'admin.participant.store',
            'internal' => request()->user_type === 'internal' ? User::TYPE_INTERNAL : User::TYPE_PARTICIPANT,
        ]);

        return view('Core::admin.participants.create', compact('form'));
    }

    /**
     * Save the New User Add page.
     *
     * @return RedirectResponse
     */
    public function store()
    {
        $form = $this->form(ProfileForm::class);

        $form->redirectIfNotValid();

        try {
            $participant = $this->participantRepository->create($form->getFieldValues());
            // TODO: Move to a language file
            event(new Registered($participant->user));
            $this->flash('success', 'Participant created successfully.');
            return redirect()->route('admin.participant.show', $participant->getKey());
        } catch (Exception $e) {
            $this->logException($e);
            // TODO: Move to a language file
            $this->flash('error', 'We were unable to create the participant. Please contact IT.');

            return redirect()->back();
        }
    }

    /**
     * Show the Participant Edit page.
     *
     * @param  int $userId
     * @return View
     * @throws PresenterException
     */
    public function edit($userId)
    {
        $participant = $this->participantRepository->find($userId);

        $form = $this->form(ProfileForm::class, [
            'method' => 'POST',
            'url' => route('admin.participant.update', $userId),
            'model' => $participant
        ]);

        $participant = $participant->present();

        return view('Core::admin.participants.edit', compact('form', 'participant'));
    }

    /**
     * Save the Participant Edit page.
     *
     * @param  int $userId
     * @return RedirectResponse
     */
    public function update($userId)
    {
        $form = $this->form(ProfileForm::class, [
            'model' => $this->participantRepository->find($userId)
        ]);

        $form->redirectIfNotValid();

        try {
            $participant = $this->participantRepository->update($userId, $form->getFieldValues(true));
            // TODO: Move to a language file
            $this->flash('success', 'Participant updated successfully.');
            return redirect()->route('admin.participant.show', $participant->getKey());
        } catch (Exception $e) {
            $this->logException($e);
            // TODO: Move to a language file
            $this->flash('error', 'We were unable to update the participant. Please contact IT.');
            return redirect()->back();
        }
    }

    /**
     * Show the participant view page.
     *
     * @param int $userId
     *
     * @return View
     * @throws PresenterException
     */
    public function show($userId)
    {
        $participant = $this->participantRepository->find($userId);

        $form = $this->form(ProfileForm::class, [
            'model' => $participant
        ], [
            'readonly' => true
        ]);

        $participant = $participant->present();

        return view('Core::admin.participants.show', compact('participant', 'form'));
    }

    /**
     * Show the delete confirmation view.
     *
     * @param int $userId
     *
     * @return View
     * @throws PresenterException
     */
    public function delete($userId)
    {
        $participant = $this->participantRepository->find($userId)->present();

        return view('Core::admin.participants.delete', compact('participant'));
    }

    /**
     * Trash the record.
     *
     * @param  int $userId
     * @return RedirectResponse
     * @throws Exception
     */
    public function destroy($userId)
    {
        DB::beginTransaction();
        try {
            $participant = $this->participantRepository->find($userId, true);
            $this->participantRepository->trash($userId);
            DB::commit();
            $this->flash('success', sprintf('%s was successfully trashed.', $participant->fullName()));
            return redirect()->route('admin.participant.index');
        } catch (Exception $e) {
            DB::rollback();
            $this->flash('error', $e->getMessage());
            return redirect()->route('admin.participant.index');
        }
    }
}
