<?php

namespace Modules\Event\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Modules\Event\Entities\Event;
use Modules\Event\Http\Requests\EventRequest;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Event\Entities\EventPhoto;
use Modules\Event\Entities\EventSchedule;
use Modules\Event\Exports\EventsExport;
use Modules\Speaker\Entities\Speaker;
use Symfony\Component\HttpFoundation\Response;
use Modules\Event\Transformers\EventCollection;

class AdminController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(Event::class, 'event');
    }

    /**
     * Display a listing of the resource.
     * @return Renderable
     */
    public function index(Request $request)
    {
        if ($request->ajax()) {
            $data = Event::query()
                ->withCount(['speakers', 'participants', 'feedbacks', 'photos'])
                ->filters($request);

            return Event::makeDataTable($data);
        }

        return view('event::admin.index', [
            'speakers' => Speaker::has('events')->orderBy('name')->pluck('name', 'id'),
        ]);
    }

    public function trashed(Request $request)
    {
        $this->authorize('restore', Event::class);

        if ($request->ajax()) {
            $data = Event::query()
                ->withCount(['speakers', 'participants', 'feedbacks', 'photos'])
                ->onlyTrashed();

            return Event::makeDataTable($data, trash: true);
        }

        return view('event::admin.trashed');
    }

    /**
     * Show the form for creating a new resource.
     * @return Renderable
     */
    public function create()
    {
        return view('event::admin.create', [
            'event' => new Event(),
            'speakers' => Speaker::active()->orderBy('name')->pluck('name', 'id'),
            'events' => Event::active()->orderBy('title')->pluck('title', 'id')
        ]);
    }

    /**
     * Store a newly created event in storage
     *
     * @param  \Modules\Event\Http\Requests\EventRequest $request
     * @param  \Modules\Event\Entities\Event $event
     * @return \Illuminate\Http\RedirectResponse
     */
    public function store(EventRequest $request, Event $event)
    {
        $inputs = $request->validated();

        unset($inputs['speakers']);
        unset($inputs['file']);
        unset($inputs['description']);

        $new_event = $event->create(array_merge($inputs, [
            'description' => remove_break_new_lines(str_replace(['<p></p>', '<div></div>'], '<br>', $request->input('description'))),
            'thumbnail' => upload_file($request, 'thumbnail', 'events/thumbnails'),
            'banner' => upload_file($request, 'banner', 'events/banners'),
            'file_name' => get_uploaded_file_name($request, 'file'),
            'file_path' => upload_file($request, 'file', 'events/documents'),
            'is_featured' => $request->boolean('is_featured'),
            'is_active' => $request->boolean('is_active'),
            'enable_submit_feedback' => $request->boolean('enable_submit_feedback'),
        ]));

        $new_event->speakers()->attach($request->input('speakers'));
        $new_event->relatedEvents()->attach($request->input('related_events'));

        return redirect()->route('event.index')->withStatus(__('New event successfully created.'));
    }

    /**
     * Show the form for editing the specified event
     *
     * @param  \Modules\Event\Entities\Event $event
     * @return \Illuminate\View\View
     */
    public function edit(Event $event)
    {
        return view('event::admin.edit', [
            'event' => $event,
            'speakers' => Speaker::active()->orderBy('name')->pluck('name', 'id'),
            'events' => Event::active()->orderBy('title')->pluck('title', 'id')
        ]);
    }

    /**
     * Update the specified event in storage
     *
     * @param  \Modules\Event\Http\Requests\EventRequest $request
     * @param  \Modules\Event\Entities\Event $event
     * @return \Illuminate\Http\RedirectResponse
     */
    public function update(EventRequest $request, Event $event)
    {
        $inputs = $request->validated();

        unset($inputs['speakers']);
        unset($inputs['file']);
        unset($inputs['description']);

        $event->update(array_merge($inputs, [
            'description' => remove_break_new_lines(str_replace(['<p></p>', '<div></div>'], '<br>', $request->input('description'))),
            'thumbnail' => upload_file($request, 'thumbnail', 'events/thumbnails', $event->thumbnail),
            'banner' => upload_file($request, 'banner', 'events/banners', $event->banner),
            'file_name' => get_uploaded_file_name($request, 'file', $event->file_name),
            'file_path' => upload_file($request, 'file', 'events/documents', $event->file_path),
            'is_featured' => $request->boolean('is_featured'),
            'is_active' => $request->boolean('is_active'),
            'enable_submit_feedback' => $request->boolean('enable_submit_feedback'),
        ]));

        $event->speakers()->sync($request->input('speakers'));
        $event->relatedEvents()->sync($request->input('related_events'));

        return redirect()->route('event.index')->withStatus(__("Event #$event->id successfully updated."));
    }

    /**
     * Remove the specified event from storage
     *
     * @param  \Modules\Event\Entities\Event $event
     * @return \Illuminate\Http\RedirectResponse
     */
    public function destroy(Event $event)
    {
        $event->delete();

        return redirect()->route('event.index')->withStatus(__("Event #$event->id successfully deleted."));
    }

    public function restore($id)
    {
        $event = Event::onlyTrashed()->findOrFail($id);

        $this->authorize('restore', $event);

        if ($event->trashed()) {
            $event->restore();

            return redirect()->route('event.trashed')->withStatus(__("Event #$event->id successfully restored."));
        }

        return redirect()->route('event.trashed')->withErrors(__("Event #$event->id cannot be restored."));
    }

    public function export(Request $request)
    {
        $this->authorize('export', Event::class);

        return Excel::download(new EventsExport($request), 'events-' . now()->format('dmY_His') . '.xlsx');
    }

    public function indexPhoto(Request $request, Event $event)
    {
        $this->authorize('view-any', $event);

        if ($request->ajax()) {
            $data = $event->photos()
                ->filters($request);

            return EventPhoto::makeDataTable($data, $event);
        }

        return view('event::admin.gallery.index', [
            'event' => $event,
            'photos' => $event->photos()->orderBy('position')->paginate(10),
        ]);
    }

    public function editPhoto(Event $event, EventPhoto $photo)
    {
        return view('event::admin.gallery.edit', [
            'event' => $event,
            'photo' => $photo,
        ]);
    }

    public function updatePhoto(Request $request, Event $event, EventPhoto $photo)
    {
        $this->authorize('view-any', $event);

        $request->validate([
            'thumbnail' => file_validation($thumbnail = $request->get('thumbnail'), required: !str_starts_with($photo->mime, 'image/'), mimes: 'png,jpeg,jpg', sometimes: false),
            'file' => file_validation($file = $request->get('file'), required: is_null($photo->path), mimes: 'png,jpeg,jpg,mp4,mov', sometimes: false),
        ]);

        $inputs = [];

        if ($request->hasFile('file') || $file) {
            $inputs = [
                'path' => $new_path = upload_file($request, 'file', 'events/gallery'),
                'name' => get_uploaded_file_name($request, 'file'),
                'extension' => $request->file('file')->extension(),
                'mime' => disk_public()->mimeType($new_path),
                'size' => disk_public()->size($new_path),
            ];
        }

        if ($request->hasFile('thumbnail') || $thumbnail) {
            $inputs['thumbnail'] = upload_file($request, 'thumbnail', 'events/gallery/thumbnails');
        }

        $photo->update($inputs);

        return redirect()->route('event.gallery.index', $event)->withStatus(__("Event gallery #$photo->id successfully updated."));
    }

    public function uploadPhoto(Request $request, Event $event)
    {
        $this->authorize('view-any', $event);

        $validator = Validator::make($request->all(), [
            'file' => file_validation(required: true, mimes: 'png,jpeg,jpg,mp4,mov', sometimes: false),
        ]);

        if ($validator->fails()) {
            return response($validator->errors()->first('file'), Response::HTTP_UNPROCESSABLE_ENTITY);
        }

        $photo = $event->photos()->create([
            'path' => $new_path = upload_file($request, 'file', 'events/gallery'),
            'name' => get_uploaded_file_name($request, 'file'),
            'extension' => $request->file('file')->extension(),
            'mime' => disk_public()->mimeType($new_path),
            'size' => disk_public()->size($new_path),
            'created_by' => admin()?->id,
        ]);

        if ($request->ajax()) {
            return response("[SUCCESS] $photo->name");
        } else {
            return redirect()->route('event.gallery.index', $event)->withStatus(__("New event photo/video successfully uploaded."));
        }
    }

    public function destroyPhoto(Request $request, Event $event, EventPhoto $photo)
    {
        $this->authorize('view-any', $event);

        destroy_asset($photo->path);

        $photo->delete();

        return redirect()->route('event.gallery.index', $event)->withStatus(__("Event photo/video successfully deleted."));
    }

    public function indexSchedule(Request $request, Event $event)
    {
        $this->authorize('view-any', $event);

        $schedules = $event->schedules()->withCount('speakers')->get()->groupBy('date');
        $dates = $event->dateRange(empty: true)->reject(fn($d, $key) => $schedules->has($key));

        return view('event::admin.schedule.index', [
            'event' => $event,
            'speakers' => Speaker::active()->orderBy('name')->pluck('name', 'id'),
            'schedules' => $dates->merge($schedules)->sortKeys(),
        ]);
    }

    public function storeSchedule(Request $request, Event $event)
    {
        $this->authorize('view-any', $event);

        $inputs = $this->validateSchedule($request, $event);

        unset($inputs['speakers']);

        $inputs['description'] = remove_break_new_lines(str_replace(['<p></p>', '<div></div>'], '<br>', $request->input('description')));
        $inputs['is_active'] = $request->boolean('is_active');
        $inputs['created_by'] = admin()?->id;

        $agenda = $event->schedules()->create($inputs);

        $agenda->speakers()->attach($request->input('speakers'));

        return redirect()->route('event.schedule.index', $event)->withStatus(__("New event #$event->id schedule successfully added."));
    }

    public function updateSchedule(Request $request, Event $event)
    {
        $this->authorize('view-any', $event);

        $processed_ids = [];

        foreach (json_decode($request->get('agendas', '[]')) as $obj) {
            $array = (array) $obj;
            $id = $array['id'];

            if (!in_array($id, $processed_ids)) {
                $event->schedules()->where('id', $id)->update($array);
                array_push($processed_ids, $id);
            }
        }

        return redirect()->route('event.schedule.index', $event)->withStatus(__("Event #$event->id schedule successfully updated."));
    }

    public function formSchedule(Request $request, Event $event, EventSchedule $schedule)
    {
        $this->authorize('view-any', $event);

        try {
            return view('event::admin.schedule.form', [
                'bag' => 'modal',
                'form' => true,
                'event' => $event,
                'schedule' => $schedule,
                'speakers' => Speaker::active()->orderBy('name')->pluck('name', 'id'),
            ])->render();
        } catch (\Exception $e) {
            report($e);
            return response($e->getMessage(), Response::HTTP_BAD_REQUEST);
        }
    }

    public function editSchedule(Request $request, Event $event, EventSchedule $schedule)
    {
        $this->authorize('view-any', $event);

        $inputs = $this->validateSchedule($request, $event, 'modal');

        unset($inputs['speakers']);

        $inputs['description'] = remove_break_new_lines(str_replace(['<p></p>', '<div></div>'], '<br>', $request->input('description')));
        $inputs['is_active'] = $request->boolean('is_active');

        $schedule->update($inputs);
        $schedule->speakers()->sync($request->input('speakers'));

        return redirect()->route('event.schedule.index', $event)->withStatus(__("Event #$event->id schedule #$schedule->id successfully updated."));
    }

    public function destroySchedule(Request $request, Event $event, EventSchedule $schedule)
    {
        $this->authorize('view-any', $event);

        $schedule->delete();

        return redirect()->route('event.schedule.index', $event)->withStatus(__("Event #$event->id schedule #$schedule->id successfully deleted."));
    }

    private function validateSchedule(Request $request, Event $event, $bag = 'default')
    {
        return $request->validateWithBag($bag, [
            'date' => ['required', 'date', Rule::in($event->dateRange()->keys())],
            'title' => ['required', 'string'],
            'description' => ['nullable', 'string'],
            'tags' => ['nullable', Rule::in(array_keys(EventSchedule::listTags()))],
            'venue' => ['nullable', 'string', 'max:100'],
            'start_time' => ['required', Rule::date()->format('H:i')],
            'end_time' => ['required', Rule::date()->format('H:i')],
            'is_active' => ['sometimes', 'boolean'],
            'speakers' => ['nullable', 'array', 'min:1'],
            'speakers.*' => [Rule::exists('speakers', 'id')->where('is_active', 1)],
        ]);
    }
}
