<?php

namespace App\Branch\Service;

use App\Branch\Entity\Room;
use App\Doctor\Entity\DoctorAddress;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Exception\AppException;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\Attribute\AutowireIterator;

final class RoomService
{
    /**
     * @param iterable<RoomDeletionGuardInterface> $deletionGuards
     */
    public function __construct(
        private readonly EntityManagerInterface $em,
        #[AutowireIterator('app.room_deletion_guard')]
        private readonly iterable               $deletionGuards = [],
    ) {}

    /** @param array<string, mixed> $data */
    public function create(DoctorAddress $address, array $data): Room
    {
        $room = new Room($address, $this->assertName($data['name'] ?? null));
        $this->applyOptional($room, $data);

        $this->em->persist($room);
        $this->em->flush();

        return $room;
    }

    /** @param array<string, mixed> $data */
    public function update(Room $room, array $data): Room
    {
        if (array_key_exists('name', $data)) {
            $room->setName($this->assertName($data['name']));
        }

        $this->applyOptional($room, $data);
        $this->em->flush();

        return $room;
    }

    public function delete(Room $room): void
    {
        foreach ($this->deletionGuards as $guard) {
            $guard->assertDeletable($room);
        }

        $this->em->remove($room);
        $this->em->flush();
    }

    /** @param array<string, mixed> $data */
    private function applyOptional(Room $room, array $data): void
    {
        if (array_key_exists('capacity', $data)) {
            $room->setCapacity($this->assertCapacity($data['capacity']));
        }

        if (array_key_exists('room_type', $data)) {
            $room->setRoomType($this->trimOrNull($data['room_type']));
        }

        if (array_key_exists('floor', $data)) {
            $room->setFloor($this->trimOrNull($data['floor']));
        }

        if (array_key_exists('active', $data)) {
            $room->setActive((bool) $data['active']);
        }
    }

    private function assertName(mixed $value): string
    {
        $name = is_string($value) ? trim($value) : '';

        if ($name === '') {
            throw new AppException(ErrorCodes::ERR_VALIDATION_002, 'نام اتاق الزامی است', 422, 'name');
        }

        if (mb_strlen($name) > 120) {
            throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'نام اتاق حداکثر ۱۲۰ نویسه است', 422, 'name');
        }

        return $name;
    }

    private function assertCapacity(mixed $value): int
    {
        if (!is_numeric($value) || (int) $value < 1) {
            throw new AppException(ErrorCodes::ERR_VALIDATION_001, 'ظرفیت اتاق حداقل ۱ است', 422, 'capacity');
        }

        return (int) $value;
    }

    private function trimOrNull(mixed $value): ?string
    {
        if (!is_string($value)) {
            return null;
        }

        $trimmed = trim($value);

        return $trimmed === '' ? null : $trimmed;
    }
}
