<?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 HourWorkbenchDailyTotalsController extends Controller
{
    private const MAX_SPAN_DAYS = 395;

    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 ('.self::MAX_SPAN_DAYS.' days).'],
            ]);
        }

        /** @var array<string, float> $totalsByDate */
        $totalsByDate = [];
        $cursor = $from->copy();
        while ($cursor->lte($to)) {
            $totalsByDate[$cursor->toDateString()] = 0.0;
            $cursor->addDay();
        }

        $weekStartsOnOrBefore = $to->toDateString();
        $weekStartsOnOrAfter = $from->copy()->subDays(6)->toDateString();

        $weeks = HourTimesheetWeek::query()
            ->where('user_id', $request->user()->getAuthIdentifier())
            ->whereDate('week_start', '<=', $weekStartsOnOrBefore)
            ->whereDate('week_start', '>=', $weekStartsOnOrAfter)
            ->with('lines')
            ->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, $totalsByDate)) {
                        continue;
                    }

                    /** @phpstan-ignore-next-line */
                    $totalsByDate[$calendarDay] += (float) $line->{'duration_'.$dayKey};
                }
            }
        }

        $rows = [];
        foreach ($totalsByDate as $date => $hours) {
            $rows[] = ['date' => $date, 'hours' => round($hours, 2)];
        }

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