<?php

namespace App\Report\Controller;

use App\Auth\Entity\User;
use App\Branch\Service\BranchResolver;
use App\Report\Service\PlanAccuracyReporter;
use App\Report\Service\ResourceUtilizationReporter;
use App\Resource\Repository\ClinicResourceRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\Shared\Event\Entity\DomainEventLog;
use App\Shared\Event\Repository\DomainEventLogRepository;
use OpenApi\Attributes as OA;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[OA\Tag(name: 'Report')]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
class ReportController extends BaseController
{
    /** بازهٔ بزرگ‌تر از این، هم کند است هم عملاً خوانده نمی‌شود. */
    private const MAX_RANGE_DAYS = 90;

    public function __construct(
        private readonly ResourceUtilizationReporter $utilization,
        private readonly PlanAccuracyReporter        $accuracy,
        private readonly ClinicResourceRepository    $resources,
        private readonly DomainEventLogRepository    $events,
        private readonly BranchResolver              $branches,
    ) {}

    #[Route('/api/v1/reports/resource-utilization', name: 'report_resource_utilization', methods: ['GET'])]
    public function resourceUtilization(#[CurrentUser] User $user, Request $request): JsonResponse
    {
        $branch = $request->query->get('branch_uuid');

        if (!is_string($branch)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'فیلد branch_uuid الزامی است', 422, 'branch_uuid');
        }

        $range = $this->range($request);

        if ($range === null) {
            return $this->rangeError();
        }

        [$from, $to] = $range;

        $address = $this->branches->resolve($user, $branch);

        return $this->success([
            'from' => $from,
            'to'   => $to,
            'rows' => $this->utilization->report(
                $this->resources->findForAddress($address),
                $address,
                $from,
                $to,
            ),
        ]);
    }

    #[Route('/api/v1/reports/plan-accuracy', name: 'report_plan_accuracy', methods: ['GET'])]
    public function planAccuracy(#[CurrentUser] User $user, Request $request): JsonResponse
    {
        $range = $this->range($request);

        if ($range === null) {
            return $this->rangeError();
        }

        [$from, $to] = $range;
        [$entityType, $entityId] = $this->branches->pair($user);

        return $this->success([
            'from' => $from,
            'to'   => $to,
            'rows' => $this->accuracy->report($entityType, $entityId, $from, $to),
        ]);
    }

    /** عیب‌یابی صندوق خروجی — فقط ادمین. */
    #[Route('/api/v1/domain-events', name: 'domain_events_index', methods: ['GET'])]
    #[IsGranted('ROLE_ADMIN')]
    public function domainEvents(Request $request): JsonResponse
    {
        $name = $request->query->get('name');

        return $this->success(array_map(
            static fn (DomainEventLog $e): array => $e->toArray(),
            $this->events->search(
                is_string($name) ? $name : null,
                null,
                null,
                $request->query->getInt('limit', 100),
            ),
        ));
    }

    /** @return array{0: int, 1: int}|null `null` یعنی بازه نامعتبر است */
    private function range(Request $request): ?array
    {
        $to   = $request->query->has('to') ? $request->query->getInt('to') : time();
        $from = $request->query->has('from') ? $request->query->getInt('from') : $to - 7 * 86400;

        if ($to <= $from || ($to - $from) > self::MAX_RANGE_DAYS * 86400) {
            return null;
        }

        return [$from, $to];
    }

    private function rangeError(): JsonResponse
    {
        return $this->error(
            ErrorCodes::ERR_VALIDATION_001,
            sprintf('بازهٔ گزارش باید مثبت و حداکثر %d روز باشد', self::MAX_RANGE_DAYS),
            422,
            'from',
        );
    }
}
