<?php

namespace App\Package\Controller;

use App\Auth\Entity\User;
use App\Branch\Service\BranchResolver;
use App\ClinicService\Entity\ServiceItem;
use App\ClinicService\Repository\ServiceItemRepository;
use App\Package\Entity\Package;
use App\Package\Entity\PackageService;
use App\Package\Repository\PackageRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
use App\Shared\Exception\AppException;
use App\Shared\Tenant\TenantOwnershipChecker;
use Doctrine\ORM\EntityManagerInterface;
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: 'Package')]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
class PackageController extends BaseController
{
    public function __construct(
        private readonly PackageRepository       $packages,
        private readonly ServiceItemRepository   $items,
        private readonly BranchResolver          $branches,
        private readonly TenantOwnershipChecker  $ownership,
        private readonly EntityManagerInterface  $em,
    ) {}

    #[Route('/api/v1/packages', name: 'package_index', methods: ['GET'])]
    public function index(#[CurrentUser] User $user, Request $request): JsonResponse
    {
        [$entityType, $entityId] = $this->branches->pair($user);

        $active = $request->query->has('active')
            ? $request->query->getBoolean('active')
            : null;

        return $this->success(array_map(
            static fn (Package $p): array => $p->toArray(),
            $this->packages->findForPair($entityType, $entityId, $active),
        ));
    }

    #[Route('/api/v1/packages', name: 'package_create', methods: ['POST'])]
    public function create(#[CurrentUser] User $user, Request $request): JsonResponse
    {
        $data = json_decode($request->getContent(), true);

        if (!is_array($data) || !is_string($data['name'] ?? null) || trim($data['name']) === '') {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'نام پکیج الزامی است', 422, 'name');
        }

        if (!is_numeric($data['session_count'] ?? null) || (int) $data['session_count'] < 1) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'تعداد جلسه باید حداقل ۱ باشد', 422, 'session_count');
        }

        $services = $this->resolveServices($user, $data['service_uuids'] ?? []);

        // پکیجی که هیچ سرویسی را پوشش نمی‌دهد هرگز قابل مصرف نیست؛ ساختنش فقط
        // یک تلهٔ خاموش برای اپراتور است.
        if ($services === []) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'پکیج باید حداقل یک سرویس داشته باشد', 422, 'service_uuids');
        }

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

        $package = new Package($entityType, $entityId, trim($data['name']), (int) $data['session_count']);
        $this->apply($package, $data);

        foreach ($services as $service) {
            $this->em->persist(new PackageService($package, $service));
        }

        $this->packages->save($package);

        return $this->success($package->toArray(), 201);
    }

    #[Route('/api/v1/package/{uuid}', name: 'package_show', methods: ['GET'])]
    public function show(#[CurrentUser] User $user, string $uuid): JsonResponse
    {
        return $this->success($this->requirePackage($user, $uuid)->toArray());
    }

    #[Route('/api/v1/package/{uuid}', name: 'package_update', methods: ['PATCH'])]
    public function update(#[CurrentUser] User $user, string $uuid, Request $request): JsonResponse
    {
        $package = $this->requirePackage($user, $uuid);
        $data    = json_decode($request->getContent(), true);

        if (!is_array($data)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'بدنهٔ درخواست نامعتبر است', 422);
        }

        if (is_string($data['name'] ?? null) && trim($data['name']) !== '') {
            $package->setName(trim($data['name']));
        }

        if (is_numeric($data['session_count'] ?? null)) {
            $package->setSessionCount((int) $data['session_count']);
        }

        $this->apply($package, $data);

        if (isset($data['service_uuids'])) {
            $services = $this->resolveServices($user, $data['service_uuids']);

            if ($services === []) {
                return $this->error(ErrorCodes::ERR_VALIDATION_002, 'پکیج باید حداقل یک سرویس داشته باشد', 422, 'service_uuids');
            }

            $package->getServices()->clear();

            foreach ($services as $service) {
                $this->em->persist(new PackageService($package, $service));
            }
        }

        $this->packages->save($package);

        return $this->success($package->toArray());
    }

    /**
     * حذف = غیرفعال کردن.
     *
     * پکیجی که فروخته شده حذف‌شدنی نیست؛ ردیف‌های دفتر به آن ارجاع دارند و حذفش
     * یعنی تاریخچهٔ اعتبار بیماران بی‌معنا شود.
     */
    #[Route('/api/v1/package/{uuid}', name: 'package_delete', methods: ['DELETE'])]
    public function delete(#[CurrentUser] User $user, string $uuid): JsonResponse
    {
        $package = $this->requirePackage($user, $uuid)->setActive(false);
        $this->packages->save($package);

        return $this->success($package->toArray());
    }

    /** @param array<string, mixed> $data */
    private function apply(Package $package, array $data): void
    {
        if (is_numeric($data['price_rials'] ?? null)) {
            $package->setPriceRials((int) $data['price_rials']);
        }

        if (array_key_exists('validity_days', $data)) {
            $package->setValidityDays(is_numeric($data['validity_days']) ? (int) $data['validity_days'] : null);
        }

        if (isset($data['active'])) {
            $package->setActive((bool) $data['active']);
        }
    }

    /**
     * @param mixed $uuids
     * @return list<ServiceItem>
     */
    private function resolveServices(User $user, mixed $uuids): array
    {
        if (!is_array($uuids)) {
            return [];
        }

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

        foreach ($uuids as $uuid) {
            if (!is_string($uuid)) {
                continue;
            }

            $item = $this->items->findByUuid($uuid);

            if ($item === null
                || $item->getSection()->getEntityType() !== $entityType
                || $item->getSection()->getEntityId() !== $entityId
            ) {
                throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, 'سرویس یافت نشد', 404);
            }

            $services[(int) $item->getId()] = $item;
        }

        return array_values($services);
    }

    private function requirePackage(User $user, string $uuid): Package
    {
        $package = $this->packages->findByUuid($uuid);
        [$entityType, $entityId] = $this->branches->pair($user);

        if ($package === null || !$this->ownership->belongsToPair($entityType, $entityId, $package)) {
            throw new AppException(ErrorCodes::ERR_NOT_FOUND_001, 'پکیج یافت نشد', 404);
        }

        return $package;
    }
}
