<?php

declare(strict_types=1);

namespace Maestro\Core\Modules\HourRegistration\Services;

use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Notification;
use Illuminate\Validation\ValidationException;
use Maestro\Core\Modules\HourRegistration\Enums\HourLineApprovalStatus;
use Maestro\Core\Modules\HourRegistration\Enums\HourRegistrationType;
use Maestro\Core\Modules\HourRegistration\Enums\HourWorkflowStatus;
use Maestro\Core\Modules\HourRegistration\Models\HourTimesheetLine;
use Maestro\Core\Modules\HourRegistration\Models\HourTimesheetWeek;
use Maestro\Core\Modules\HourRegistration\Notifications\HourWeekAwaitingApprovalNotification;
use Maestro\Core\Support\AuthUserResolver;

final class HourSubmitWeekService
{
    public function __construct(
        private readonly HourWeekSubmissionWarningsService $warningsService,
        private readonly HourApproverResolver $approverResolver,
        private readonly HourWeekWorkflowStatusService $workflowStatusService,
    ) {}

    /**
     * @return array{warnings:list<string>, data: HourTimesheetWeek}
     */
    public function submit(HourTimesheetWeek $week): array
    {
        $user = Auth::guard()->user();
        abort_unless($user, 403);
        abort_unless((string) $week->user_id === (string) $user->getAuthIdentifier(), 403);
        abort_unless($user->can('hours.week.manage'), 403);

        if ($week->workflow_status === HourWorkflowStatus::Approved) {
            throw ValidationException::withMessages([
                'week' => ['This week is already approved and locked.'],
            ]);
        }

        $week->loadMissing(['lines']);

        if ($week->lines->isEmpty()) {
            throw ValidationException::withMessages([
                'lines' => ['Add at least one timesheet row before submitting.'],
            ]);
        }

        $warnings = $this->warningsService->collect($week->lines);

        DB::transaction(function () use ($week): void {
            $lines = HourTimesheetLine::query()->where('hour_timesheet_week_id', $week->id)->get();

            foreach ($lines as $line) {
                if ($line->registration_type === HourRegistrationType::Internal) {
                    $line->line_approval_status = HourLineApprovalStatus::Approved;
                    $line->approved_by_id = null;
                    $line->approved_at = now();
                    $line->reject_reason = null;
                    $line->save();

                    continue;
                }

                $approverId = $this->approverResolver->resolveApproverUserId($line);
                if ($approverId === null) {
                    throw ValidationException::withMessages([
                        'lines' => [
                            sprintf(
                                'No approver is configured for line #%d (%s). Set a project lead, customer project manager, or task lead.',
                                (int) $line->id,
                                $line->registration_type->value,
                            ),
                        ],
                    ]);
                }

                $line->line_approval_status = HourLineApprovalStatus::Pending;
                $line->approved_by_id = null;
                $line->approved_at = null;
                $line->reject_reason = null;
                $line->save();
            }

            $week->workflow_status = HourWorkflowStatus::Submitted;
            $week->submitted_at = now();
            $week->save();

            $fresh = $week->fresh(['lines']);
            if ($fresh !== null) {
                $this->workflowStatusService->refreshAggregateStatus($fresh);
            }
        });

        $week = $week->fresh(['lines']);
        abort_unless($week !== null, 500);

        $approverIds = $week->lines
            ->filter(fn ($l) => $this->approverResolver->lineRequiresLeadApproval($l)
                && $l->line_approval_status === HourLineApprovalStatus::Pending)
            ->map(fn ($l) => $this->approverResolver->resolveApproverUserId($l))
            ->filter()
            ->unique()
            ->values()
            ->all();

        $userClass = AuthUserResolver::eloquentModelClass();
        if ($approverIds !== []) {
            $notifiables = $userClass::query()->whereIn('id', $approverIds)->get();
            Notification::send($notifiables, new HourWeekAwaitingApprovalNotification($week));
        }

        return [
            'warnings' => $warnings,
            'data' => $week,
        ];
    }
}
