<?php

namespace App\Branch\Controller;

use App\Auth\Entity\User;
use App\Branch\Repository\BranchWorkingHoursRepository;
use App\Branch\Repository\RoomRepository;
use App\Branch\Service\BranchResolver;
use App\Branch\Service\WorkingHoursService;
use App\Clinic\Security\ClinicDoctorAccessChecker;
use App\Doctor\Entity\DoctorAddress;
use App\Secretary\Security\SecretaryAccessChecker;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
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;

/**
 * شعبه = {@see DoctorAddress}. ساختن/ویرایش/حذف آدرس از قبل در ClinicController و
 * AppointmentSettingsController هست و اینجا تکرار نمی‌شود؛ این کنترلر فقط چیزهایی را
 * می‌دهد که آنجا نیست: فهرست شعبه‌های محیط جاری با شمارش، دو ویژگی تازهٔ
 * active/timezone، و ساعت کاری هفتگی.
 */
#[OA\Tag(name: 'Branch')]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
class BranchController extends BaseController
{
    public function __construct(
        private readonly BranchResolver               $branches,
        private readonly WorkingHoursService          $workingHours,
        private readonly BranchWorkingHoursRepository $hoursRepo,
        private readonly RoomRepository               $roomRepo,
        private readonly EntityManagerInterface       $em,
        private readonly SecretaryAccessChecker       $secretaryAccess,
        private readonly ClinicDoctorAccessChecker    $clinicDoctorAccess,
    ) {}

    /** @param 'view'|'update' $action */
    private function denyUnlessGranted(User $user, string $action): void
    {
        $this->secretaryAccess->denyUnlessGranted($user, 'appointment_settings', $action);
        $this->clinicDoctorAccess->denyUnlessGranted($user, 'appointment_settings', $action);
    }

    #[Route('/api/v1/branches', name: 'branch_list', methods: ['GET'])]
    public function list(#[CurrentUser] User $user): JsonResponse
    {
        $this->denyUnlessGranted($user, 'view');

        $addresses = $this->branches->listForContext($user);
        $ids       = array_map(static fn (DoctorAddress $a): int => (int) $a->getId(), $addresses);

        // دو کوئری گروهی به‌جای دو کوئری per شعبه.
        $hourCounts = $this->hoursRepo->countByAddressIds($ids);
        $roomCounts = $this->roomRepo->countActiveByAddressIds($ids);

        $rows = array_map(static function (DoctorAddress $address) use ($hourCounts, $roomCounts): array {
            $id  = (int) $address->getId();
            $row = $address->toArray();

            $row['working_hours_defined'] = ($hourCounts[$id] ?? 0) > 0;
            $row['rooms_count']           = $roomCounts[$id] ?? 0;

            return $row;
        }, $addresses);

        return $this->success($rows);
    }

    /** فقط دو ویژگی شعبه‌ای؛ نام/آدرس/تلفن همان‌جایی ویرایش می‌شوند که همیشه. */
    #[Route('/api/v1/branch/{addressUuid}', name: 'branch_update', methods: ['PATCH'])]
    public function update(#[CurrentUser] User $user, string $addressUuid, Request $request): JsonResponse
    {
        $this->denyUnlessGranted($user, 'update');

        $address = $this->branches->resolve($user, $addressUuid);
        $data    = json_decode($request->getContent(), true);

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

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

        if (array_key_exists('timezone', $data)) {
            if (!is_string($data['timezone'])) {
                return $this->error(ErrorCodes::ERR_VALIDATION_001, 'منطقهٔ زمانی نامعتبر است', 422, 'timezone');
            }

            try {
                $address->setTimezone($data['timezone']);
            } catch (\InvalidArgumentException) {
                return $this->error(ErrorCodes::ERR_VALIDATION_001, 'منطقهٔ زمانی نامعتبر است', 422, 'timezone');
            }
        }

        $this->em->flush();

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

    #[Route('/api/v1/branch/{addressUuid}/working-hours', name: 'branch_working_hours_show', methods: ['GET'])]
    public function showWorkingHours(#[CurrentUser] User $user, string $addressUuid): JsonResponse
    {
        $this->denyUnlessGranted($user, 'view');

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

        return $this->success([
            'branch_uuid' => $address->getUuid(),
            'timezone'    => $address->getTimezone(),
            'defined'     => $this->workingHours->isDefined($address),
            'days'        => self::daysObject($this->workingHours->read($address)),
        ]);
    }

    /**
     * جایگزینی کامل هفت روز. آرایهٔ خالی یعنی شعبه کاملاً بسته است — نه «تغییری نده».
     */
    #[Route('/api/v1/branch/{addressUuid}/working-hours', name: 'branch_working_hours_replace', methods: ['PUT'])]
    public function replaceWorkingHours(#[CurrentUser] User $user, string $addressUuid, Request $request): JsonResponse
    {
        $this->denyUnlessGranted($user, 'update');

        $address = $this->branches->resolve($user, $addressUuid);
        $data    = json_decode($request->getContent(), true);

        if (!is_array($data) || !is_array($data['days'] ?? null)) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'فیلد days الزامی است', 422, 'days');
        }

        $days = $this->workingHours->replace($address, $data['days']);

        return $this->success([
            'branch_uuid' => $address->getUuid(),
            'timezone'    => $address->getTimezone(),
            'defined'     => $days !== array_fill_keys(WorkingHoursService::DAYS, []),
            'days'        => self::daysObject($days),
        ]);
    }

    /**
     * کلیدهای ۰..۶ پشت‌سرهم‌اند، پس json_encode آرایهٔ PHP را به **آرایهٔ JSON**
     * تبدیل می‌کرد نه به شیئی با کلیدهای "0".."6". کلاینت با `days["0"]` هر دو را
     * می‌خواند، ولی شکل پاسخ ناپایدار می‌شد: کافی بود یک روز جا بیفتد تا همان فیلد
     * شیء برگردد. (کلید رشته‌ایِ عددی هم چاره نیست — PHP خودش به int برش می‌گرداند.)
     *
     * @param array<int, list<array<string, mixed>>> $days
     */
    private static function daysObject(array $days): \stdClass
    {
        return (object) $days;
    }
}
