<?php

declare(strict_types=1);

namespace Maestro\Core\Modules\HourRegistration\Services;

use Carbon\Carbon;
use Illuminate\Contracts\Auth\Authenticatable;
use Maestro\Core\Modules\Customers\Models\Customer;
use Maestro\Core\Modules\HourRegistration\Enums\HourLineApprovalStatus;
use Maestro\Core\Modules\HourRegistration\Enums\HourWorkflowStatus;
use Maestro\Core\Modules\HourRegistration\Http\Controllers\HourInvoicingApiController;
use Maestro\Core\Modules\HourRegistration\Models\HourTimesheetLine;

/**
 * Stable JSON envelope for invoicing integrations (consumers MUST pin this contract).
 *
 * Endpoint docblock lives on {@see HourInvoicingApiController}.
 */
final class HourInvoicingApiService
{
    public function __construct(
        private readonly HourTotalsCalculator $totalsCalculator,
    ) {}

    /**
     * @return array<string, mixed>
     */
    public function approvedBillableHoursForCustomer(Authenticatable $user, int $customerId, string $fromIso, string $toIso): array
    {
        abort_unless($user->can('hours.invoicing.read') || $user->can('hours.admin'), 403);

        $customer = Customer::query()->findOrFail($customerId);

        $from = Carbon::parse($fromIso)->startOfDay();

        $to = Carbon::parse($toIso)->endOfDay();

        $entries = [];

        $query = HourTimesheetLine::query()
            ->with(['week.user'])
            ->where('line_approval_status', HourLineApprovalStatus::Approved)
            ->whereHas('week', function ($w) use ($from, $to): void {
                $w->where('workflow_status', HourWorkflowStatus::Approved)
                    ->whereDate('week_start', '>=', $from->toDateString())
                    ->whereDate('week_start', '<=', $to->toDateString());
            })
            ->where(function ($q) use ($customerId): void {
                $q->where('customer_id', $customerId)
                    ->orWhereHas('hourProject', fn ($p) => $p->where('customer_id', $customerId))
                    ->orWhereHas(
                        'hourTask.project',
                        fn ($p) => $p->where('customer_id', $customerId),
                    );
            })
            ->orderBy('hour_timesheet_week_id')
            ->orderBy('id');

        $grand = 0.0;
        foreach ($query->lazy(80) as $line) {
            $week = $line->week;
            $hrs = $this->totalsCalculator->lineTotalHours($line);
            $grand += $hrs;

            /** @phpstan-ignore-next-line */
            $emp = $week ? $week->user : null;

            $entries[] = [
                'employee_user_id' => $week ? (int) $week->user_id : null,
                'employee_name' => $emp ? (string) $emp->name : null,
                'week_start' => $week ? $week->week_start->toDateString() : null,
                'registration_type' => $line->registration_type->value,
                'line_id' => (int) $line->getKey(),
                'hours_decimal' => $hrs,
                'daily_breakdown_decimal' => collect(HourTimesheetLine::WEEKDAY_KEYS)->mapWithKeys(function (string $d) use ($line) {
                    return [$d => round((float) $line->{'duration_'.$d}, 2)];
                })->all(),
            ];
        }

        return [
            'contract_version' => '1',
            'customer' => [
                'id' => (int) $customer->id,
                'name' => $customer->name,
            ],
            'period_inclusive_utc_boundary' => [
                'from' => $from->toIso8601String(),
                'to' => $to->toIso8601String(),
            ],
            'only_workflow_and_line_status' => ['week:approved', 'line:approved'],
            'currency' => null,
            'entries' => $entries,
            'total_hours_decimal' => round($grand, 2),
        ];
    }
}
