<?php

namespace App\Modules\ServiceBooking;

use App\Modules\ServiceBooking\Http\Requests\BookingCalendarRequest;
use App\Modules\ServiceBooking\Http\Requests\CreateBookingRequest;
use App\Modules\ServiceBooking\Http\Requests\UpdateBookingRequest;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Mtc\MercuryDataModels\Booking;
use Mtc\MercuryDataModels\BookingStatus;

class BookingRepository
{
    private Carbon $when;

    public function create(CreateBookingRequest $request): Booking
    {
        $booking = new Booking();
        $booking->fill($request->input());
        $booking->save();
        return $booking;
    }

    public function update(Booking $booking, UpdateBookingRequest $request): void
    {
        $booking->update($request->input());
    }

    public function getCalendar(BookingCalendarRequest $request): Collection
    {
        $period = $request->input('period', 'month');
        $this->when = $request->input('from') ? Carbon::parse($request->input('from')) : Carbon::now()->startOfWeek();
        $location = $request->input('location') !== 'all' ? $request->input('location') : null;
        $search = $request->input('search');

        $from = $this->when->copy();
        if ($period !== 'day') {
            $from->startOfWeek();
        }

        $until = match ($period) {
            'week' => $from->copy()->endOfWeek(),
            default => $from->copy()->addMonth()->endOfWeek(),
        };

        $bookings = $this->getBookingsForPeriod($period, $from, $until, $location, $search);
        return  $this->getGrid($period, $from, $until, $bookings);
    }

    public function getPeriodValue(): Carbon
    {
        return $this->when;
    }

    private function getBookingsForPeriod(
        string $period,
        Carbon $from,
        Carbon $until,
        ?int $location,
        ?string $search = null
    ): Collection {
        return Booking::query()
            ->with([
                'location',
            ])
            ->where('booking_time', '>=', $from)
            ->where('booking_time', '<=', $until)
            ->when($location, fn($query) => $query->where('location_id', $location))
            ->when($search, fn($query) => $query->where(function ($q) use ($search) {
                $q->where('first_name', 'LIKE', "%{$search}%")
                    ->orWhere('last_name', 'LIKE', "%{$search}%")
                    ->orWhere('email', 'LIKE', "%{$search}%")
                    ->orWhere('phone_number', 'LIKE', "%{$search}%")
                    ->orWhere('registration_number', 'LIKE', "%{$search}%");
            }))
            ->where('status', '!=', BookingStatus::CANCELLED->value)
            ->where('status', '!=', BookingStatus::DRAFT->value)
            ->orderBy('booking_time')
            ->get()
            ->groupBy(fn($booking) => $booking->booking_time->format('Y-m-d'));
    }

    private function getGrid(string $period, Carbon $from, Carbon $until, Collection $bookings): Collection
    {
        if ($period === 'week') {
            return $this->getGridForWeek($from, $until, $bookings);
        }

        return $this->buildGrid($from, $until, $bookings);
    }

    private function buildGrid(Carbon $from, Carbon $until, Collection $bookings): Collection
    {
        $grid = collect();
        foreach (range(1, $from->diffInWeeks($until) + 1) as $week) {
            $weekOfYear = (int)$from->copy()->addWeeks($week - 1)->format('W');
            $data = collect(array_fill(0, 7, 1))
                ->map(function ($value, $index) use ($weekOfYear, $bookings, $from) {
                    $date = Carbon::create($from->format('Y'))
                        ->startOfYear()
                        ->addWeeks($weekOfYear - 1)
                        ->startOfWeek()
                        ->addDays($index);
                    return [
                        'name' => $date->format('jS M'),
                        'weekday' => $date->format('D'),
                        'week_of_year' => $date->format('W'),
                        'full-date' => $date->format('Y-m-d'),
                        'entries' => collect($bookings[$date->format('Y-m-d')] ?? [])
                            ->groupBy('location_id')
                            ->map(fn($locationData) => [
                                'name' => $locationData->first()->location?->name ?? 'Location',
                                'entries' => $locationData,
                            ])
                    ];
                });
            $grid->put($week, $data);
        }
        return $grid;
    }

    private function getGridForWeek(Carbon $from, Carbon $until, Collection $bookings)
    {
        $grid = collect();
            $data = collect(array_fill(0, 7, 1))
                ->map(function ($value, $index) use ($bookings, $from) {
                    $date = $from->copy()->addDays($index);
                    return [
                        'name' => $date->format('jS M'),
                        'weekday' => $date->format('D'),
                        'week_of_year' => $date->format('W'),
                        'full-date' => $date->format('Y-m-d'),
                        'entries' => collect($bookings[$date->format('Y-m-d')] ?? [])
                            ->groupBy('location_id')
                            ->map(fn($locationData) => [
                                'name' => $locationData->first()->location?->name ?? 'Location',
                                'entries' => $locationData,
                            ])
                    ];
                });
            $grid->put($from->format('W'), $data);
        return $grid;
    }
}
