<?php

declare(strict_types=1);

namespace Maestro\Core\Modules\HourRegistration\Http\Controllers;

use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Maestro\Core\Http\Controllers\Controller;
use Maestro\Core\Modules\HourRegistration\Models\HourTimesheetLine;
use Maestro\Core\Modules\HourRegistration\Models\HourTimesheetWeek;

final class HourMonthEntriesController extends Controller
{
    private const MAX_SPAN_DAYS = 62;

    public function __invoke(Request $request): JsonResponse
    {
        abort_unless($request->user()?->can('hours.week.manage'), 403);

        $validated = validator($request->query(), [
            'from' => ['required', 'date'],
            'to'   => ['required', 'date'],
        ])->validated();

        $from = Carbon::parse((string) $validated['from'])->startOfDay();
        $to   = Carbon::parse((string) $validated['to'])->startOfDay();

        if ($to->lt($from)) {
            throw ValidationException::withMessages(['to' => ['The to date must be on or after from.']]);
        }

        if ($from->diffInDays($to) >= self::MAX_SPAN_DAYS) {
            throw ValidationException::withMessages(['to' => ['Date range exceeds maximum allowed days.']]);
        }

        $entriesByDate = [];
        $cursor = $from->copy();
        while ($cursor->lte($to)) {
            $entriesByDate[$cursor->toDateString()] = [];
            $cursor->addDay();
        }

        $weekStartBefore = $to->toDateString();
        $weekStartAfter  = $from->copy()->subDays(6)->toDateString();

        $weeks = HourTimesheetWeek::query()
            ->where('user_id', $request->user()->getAuthIdentifier())
            ->whereDate('week_start', '<=', $weekStartBefore)
            ->whereDate('week_start', '>=', $weekStartAfter)
            ->with(['lines.internalCategory', 'lines.hourProject', 'lines.hourTask', 'lines.customer'])
            ->get();

        foreach ($weeks as $week) {
            $monday = Carbon::parse($week->week_start->toDateString())->startOfDay();

            foreach ($week->lines as $line) {
                foreach (array_values(HourTimesheetLine::WEEKDAY_KEYS) as $idx => $dayKey) {
                    $calendarDay = $monday->copy()->addDays($idx)->toDateString();

                    if (! array_key_exists($calendarDay, $entriesByDate)) {
                        continue;
                    }

                    $hours = (float) $line->{'duration_'.$dayKey};

                    if ($hours <= 0) {
                        continue;
                    }

                    $label = match ($line->registration_type->value) {
                        'internal' => $line->internalCategory?->name ?? 'Intern',
                        'project'  => $line->hourProject?->name ?? 'Project',
                        'task'     => $line->hourTask?->title ?? 'Taak',
                        'customer' => $line->customer?->name ?? 'Klant',
                        default    => 'Overig',
                    };

                    $entriesByDate[$calendarDay][] = [
                        'line_id'     => $line->id,
                        'hours'       => $hours,
                        'label'       => $label,
                        'description' => $line->description ?? '',
                    ];
                }
            }
        }

        $rows = [];
        foreach ($entriesByDate as $date => $entries) {
            $rows[] = ['date' => $date, 'entries' => $entries];
        }

        return response()->json(['data' => $rows]);
    }
}
