<?php

namespace App\Dashboard\Controller;

use App\Auth\Entity\User;
use App\Clinic\Repository\ClinicRepository;
use App\ClinicService\Entity\ServiceItem;
use App\ClinicService\Repository\ServiceItemRepository;
use App\Doctor\Repository\DoctorRepository;
use App\Patient\Repository\PatientRecordRepository;
use App\Patient\Repository\PatientSessionRepository;
use App\Secretary\Entity\DoctorSecretary;
use App\Secretary\Repository\DoctorSecretaryRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Context\EntityContextResolver;
use App\Shared\Controller\BaseController;
use App\Sms\Service\SmsWalletService;
use App\Staff\Repository\ClinicStaffRepository;
use App\Staff\Security\StaffPermissions;
use Doctrine\ORM\EntityManagerInterface;
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;
use OpenApi\Attributes as OA;

#[OA\Tag(name: 'Dashboard')]
class DashboardController extends BaseController
{
    public function __construct(
        private readonly ClinicRepository              $clinicRepo,
        private readonly DoctorRepository              $doctorRepo,
        private readonly DoctorSecretaryRepository     $secretaryRepo,
        private readonly EntityManagerInterface        $em,
        private readonly SmsWalletService              $smsWalletService,
        private readonly PatientRecordRepository       $patientRecordRepo,
        private readonly PatientSessionRepository      $patientSessionRepo,
        private readonly EntityContextResolver         $contextResolver,
        private readonly \App\Clinic\Security\ClinicDoctorPermissionChecker $permChecker,
        private readonly \App\Doctor\Repository\DoctorAddressRepository $addressRepo,
        private readonly ClinicStaffRepository         $staffRepo,
        private readonly ServiceItemRepository         $serviceItemRepo,
    ) {}

    // ── Clinic Dashboard ────────────────────────────────────────────────────

    #[Route('/api/v1/dashboard/clinic', methods: ['GET'])]
    #[IsGranted('ROLE_CLINIC')]
    public function clinic(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $clinic = $this->clinicRepo->findByUser($user);
        if ($clinic === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'کلینیک یافت نشد', 404);
        }

        $clinicId   = $clinic->getId();
        $todayStart = strtotime('today midnight');
        $todayEnd   = strtotime('tomorrow midnight') - 1;
        $monthStart = strtotime('first day of this month midnight');

        $from = $request->query->get('from') ? (int) $request->query->get('from') : $monthStart;
        $to   = $request->query->get('to')   ? (int) $request->query->get('to')   : time();

        // آمار نوبت‌های امروز و این ماه
        $stats = $this->em->createQuery('
            SELECT
                COUNT(a.id) AS today_appointments,
                SUM(CASE WHEN a.slotStart >= :monthStart THEN 1 ELSE 0 END) AS this_month_appointments
            FROM App\Appointment\Entity\Appointment a
            JOIN App\Doctor\Entity\Doctor d WITH a.doctor = d
            JOIN App\Clinic\Entity\Clinic c WITH d MEMBER OF c.doctors
            WHERE c.id = :clinicId
              AND a.slotStart >= :todayStart AND a.slotStart <= :todayEnd
        ')->setParameters([
            'clinicId'   => $clinicId,
            'todayStart' => $todayStart,
            'todayEnd'   => $todayEnd,
            'monthStart' => $monthStart,
        ])->getOneOrNullResult() ?? [];

        // شمارش کل نوبت‌های این ماه (query جداگانه)
        $monthCount = (int) $this->em->createQuery('
            SELECT COUNT(a.id)
            FROM App\Appointment\Entity\Appointment a
            JOIN App\Doctor\Entity\Doctor d WITH a.doctor = d
            JOIN App\Clinic\Entity\Clinic c WITH d MEMBER OF c.doctors
            WHERE c.id = :clinicId
              AND a.slotStart >= :monthStart
        ')->setParameters([
            'clinicId'   => $clinicId,
            'monthStart' => $monthStart,
        ])->getSingleScalarResult();

        // تعداد دعوتنامه‌های در انتظار
        $pendingInvitations = (int) $this->em->createQuery('
            SELECT COUNT(i.id)
            FROM App\ClinicInvitation\Entity\ClinicDoctorInvitation i
            WHERE i.clinic = :clinic AND i.status = :status
        ')->setParameters([
            'clinic' => $clinic,
            'status' => 'pending',
        ])->getSingleScalarResult();

        // ۵ نوبت امروز این کلینیک
        $todayAppts = $this->em->createQuery('
            SELECT a.uuid, u.realName AS patient_name, u.mobileNumber AS patient_mobile,
                   d.name AS doctor_name, si.name AS service_name,
                   a.slotStart AS slot_start, a.slotEnd AS slot_end, a.status
            FROM App\Appointment\Entity\Appointment a
            JOIN a.doctor d
            JOIN a.user u
            LEFT JOIN a.serviceItem si
            JOIN App\Clinic\Entity\Clinic c WITH d MEMBER OF c.doctors
            WHERE c.id = :clinicId
              AND a.slotStart >= :todayStart AND a.slotStart <= :todayEnd
            ORDER BY a.slotStart ASC
        ')->setMaxResults(5)->setParameters([
            'clinicId'   => $clinicId,
            'todayStart' => $todayStart,
            'todayEnd'   => $todayEnd,
        ])->getArrayResult();

        // لیست پزشکان با شمارش نوبت امروز
        $doctors = $this->em->createQuery('
            SELECT d.uuid, d.name,
                   COUNT(a.id) AS today_count
            FROM App\Doctor\Entity\Doctor d
            JOIN App\Clinic\Entity\Clinic c WITH d MEMBER OF c.doctors
            LEFT JOIN App\Appointment\Entity\Appointment a
                WITH a.doctor = d
                AND a.slotStart >= :todayStart
                AND a.slotStart <= :todayEnd
            WHERE c.id = :clinicId
            GROUP BY d.id
        ')->setParameters([
            'clinicId'   => $clinicId,
            'todayStart' => $todayStart,
            'todayEnd'   => $todayEnd,
        ])->getArrayResult();

        $smsBalance      = $this->smsWalletService->getBalance('clinic', $clinicId);
        $uniquePatients  = $this->patientRecordRepo->countUnique('clinic', $clinicId, $from, $to);
        $revenuePeriod   = $this->patientSessionRepo->sumRevenue('clinic', $clinicId, $from, $to);
        $totalPatients   = $this->patientRecordRepo->countUnique('clinic', $clinicId, 0, time());

        $chartPeriod  = $this->chartPeriodParams($request);
        $rev          = $this->revenueDaily('clinic', $clinicId);
        $revenueByMon = $this->revenueByJalaliYear('clinic', $clinicId, $chartPeriod['revenue_year']);
        $apptByDay    = $this->appointmentsByJalaliMonth(
            $chartPeriod['patients_year'],
            $chartPeriod['patients_month'],
            function (int $s, int $e) use ($clinicId): array {
                $rows = $this->em->createQuery('
                    SELECT a.slotStart FROM App\Appointment\Entity\Appointment a
                    JOIN App\Doctor\Entity\Doctor d WITH a.doctor = d
                    JOIN App\Clinic\Entity\Clinic c WITH d MEMBER OF c.doctors
                    WHERE c.id = :clinicId AND a.slotStart >= :s AND a.slotStart <= :e
                ')->setParameters(['clinicId' => $clinicId, 's' => $s, 'e' => $e])->getArrayResult();

                return array_column($rows, 'slotStart');
            }
        );

        return $this->success([
            'clinic' => [
                'uuid'      => $clinic->getUuid(),
                'name'      => $clinic->getName(),
                'is_active' => $clinic->isActive(),
                'logo'      => $clinic->getClinicLogo(),
            ],
            'stats' => [
                'total_doctors'            => count($doctors),
                'today_appointments'       => (int) ($stats['today_appointments'] ?? 0),
                'this_month_appointments'  => $monthCount,
                'pending_invitations'      => $pendingInvitations,
                'sms_wallet_balance'       => $smsBalance,
                'unique_patients_count'    => $uniquePatients,
                'total_patients'           => $totalPatients,
                'revenue_period_rials'     => $revenuePeriod,
                'today_payments_rials'     => $rev['today_payments_rials'],
                'week_payments_rials'      => $rev['week_payments_rials'],
            ],
            'charts' => [
                'revenue_by_day'      => $rev['revenue'],
                'revenue_by_month'    => $revenueByMon,
                'appointments_by_day' => $apptByDay,
            ],
            'charts_period' => $chartPeriod,
            'period' => ['from' => $from, 'to' => $to],
            'today_appointments' => $todayAppts,
            'doctors'            => $doctors,
        ]);
    }

    // ── Doctor Dashboard ─────────────────────────────────────────────────────

    #[Route('/api/v1/dashboard/doctor', methods: ['GET'])]
    #[IsGranted('ROLE_DOCTOR')]
    public function doctor(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $doctor = $this->doctorRepo->findByUser($user);
        if ($doctor === null) {
            return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'دکتر یافت نشد', 404);
        }

        // محیط فعال تعیین می‌کند این داشبورد شخصی است یا داخل یک کلینیک. بدون این،
        // پزشکِ دعوت‌شده در محیط کلینیک آمار و درآمد مطب شخصی خودش را می‌دید.
        $context = $this->contextResolver->tryResolve($user, $request->query->get('clinic_uuid'));
        $clinic  = $context?->clinic;

        $doctorId   = $doctor->getId();
        $todayStart = strtotime('today midnight');
        $todayEnd   = strtotime('tomorrow midnight') - 1;
        $tmrStart   = strtotime('tomorrow midnight');
        $tmrEnd     = strtotime('tomorrow midnight') + 86399;
        $monthStart = strtotime('first day of this month midnight');

        $from = $request->query->get('from') ? (int) $request->query->get('from') : $monthStart;
        $to   = $request->query->get('to')   ? (int) $request->query->get('to')   : time();

        // آمار
        $todayCount = $this->countAppointments($doctor, $clinic, $todayStart, $todayEnd);
        $tmrCount   = $this->countAppointments($doctor, $clinic, $tmrStart, $tmrEnd);
        $monthCount = $this->countAppointments($doctor, $clinic, $monthStart, PHP_INT_MAX);

        // میانگین و تعداد امتیاز
        $ratingRow = $this->em->createQuery('
            SELECT AVG((r.waitingTimeAtClinic + r.accuracyOfDiagnosis + r.doctorBehavior + r.clinicCleanliness + r.doctorExpertise) / 5.0) AS avg_score,
                   COUNT(r.id) AS total
            FROM App\Rating\Entity\Rate r WHERE r.doctor = :doctor
        ')->setParameter('doctor', $doctor)->getOneOrNullResult() ?? [];

        // نوبت‌های امروز
        $todayApptsQb = $this->em->createQueryBuilder()
            ->select('a.uuid, u.realName AS patient_name, u.mobileNumber AS patient_mobile,
                      d.name AS doctor_name, si.name AS service_name,
                      a.slotStart AS slot_start, a.slotEnd AS slot_end, a.status')
            ->from(\App\Appointment\Entity\Appointment::class, 'a')
            ->join('a.user', 'u')
            ->join('a.doctor', 'd')
            ->leftJoin('a.serviceItem', 'si')
            ->where('a.doctor = :doctor')
            ->andWhere('a.slotStart >= :s AND a.slotStart <= :e')
            ->setParameter('doctor', $doctor)
            ->setParameter('s', $todayStart)
            ->setParameter('e', $todayEnd)
            ->orderBy('a.slotStart', 'ASC')
            ->setMaxResults(10);

        $this->restrictToClinicAddresses($todayApptsQb, $clinic);
        $todayAppts = $todayApptsQb->getQuery()->getArrayResult();

        // کلینیک‌های عضو
        $clinics = $this->em->createQuery('
            SELECT c.uuid, c.name, c.clinicLogo AS logo
            FROM App\Clinic\Entity\Clinic c
            JOIN c.doctors d
            WHERE d.id = :doctorId
        ')->setParameter('doctorId', $doctorId)->getArrayResult();

        $chartPeriod = $this->chartPeriodParams($request);
        $apptByDay   = $this->appointmentsByJalaliMonth(
            $chartPeriod['patients_year'],
            $chartPeriod['patients_month'],
            fn(int $s, int $e): array => $this->appointmentSlotStarts($doctor, $clinic, $s, $e)
        );

        $stats = [
            'today_appointments'      => $todayCount,
            'tomorrow_appointments'   => $tmrCount,
            'this_month_appointments' => $monthCount,
            'avg_rating'              => $ratingRow['avg_score'] ? round((float) $ratingRow['avg_score'], 1) : null,
            'total_ratings'           => (int) ($ratingRow['total'] ?? 0),
        ];

        $charts = ['appointments_by_day' => $apptByDay];

        // ارقام مالی و کیف پول پیامک به مطب شخصی تعلق دارند. در محیط کلینیک اصلاً
        // برگردانده نمی‌شوند مگر کاربر مالک همان کلینیک باشد — مخفی‌کردن در UI کافی
        // نیست، چون endpoint مستقیماً قابل صدا زدن است.
        if ($this->maySeeFinancials($user, $clinic)) {
            $rev = $this->revenueDaily('doctor', $doctorId);

            $stats['sms_wallet_balance']    = $this->smsWalletService->getBalance('doctor', $doctorId);
            $stats['unique_patients_count'] = $this->patientRecordRepo->countUnique('doctor', $doctorId, $from, $to);
            $stats['total_patients']        = $this->patientRecordRepo->countUnique('doctor', $doctorId, 0, time());
            $stats['revenue_period_rials']  = $this->patientSessionRepo->sumRevenue('doctor', $doctorId, $from, $to);
            $stats['today_payments_rials']  = $rev['today_payments_rials'];
            $stats['week_payments_rials']   = $rev['week_payments_rials'];

            $charts['revenue_by_day']   = $rev['revenue'];
            $charts['revenue_by_month'] = $this->revenueByJalaliYear('doctor', $doctorId, $chartPeriod['revenue_year']);
        }

        return $this->success([
            'doctor' => [
                'uuid'   => $doctor->getUuid(),
                'name'   => $doctor->getName(),
                'degree' => $doctor->getDegree(),
            ],
            'context' => [
                'type'        => $clinic === null ? 'personal' : 'clinic',
                'clinic_uuid' => $clinic?->getUuid(),
                'clinic_name' => $clinic?->getName(),
            ],
            'stats'              => $stats,
            'charts'             => $charts,
            'charts_period'      => $chartPeriod,
            'period'             => ['from' => $from, 'to' => $to],
            'today_appointments' => $todayAppts,
            // فهرست کلینیک‌ها فقط در محیط شخصی معنا دارد.
            'clinics'            => $clinic === null ? $clinics : [],
        ]);
    }

    /**
     * نوبت‌های پزشک در یک بازه، محدود به محیط جاری. در محیط کلینیک فقط نوبت‌هایی
     * شمرده می‌شوند که آدرسشان متعلق به همان کلینیک است.
     */
    private function countAppointments(\App\Doctor\Entity\Doctor $doctor, ?\App\Clinic\Entity\Clinic $clinic, int $from, int $to): int
    {
        $qb = $this->em->createQueryBuilder()
            ->select('COUNT(a.id)')
            ->from(\App\Appointment\Entity\Appointment::class, 'a')
            ->where('a.doctor = :doctor')
            ->andWhere('a.slotStart >= :s AND a.slotStart <= :e')
            ->setParameter('doctor', $doctor)
            ->setParameter('s', $from)
            ->setParameter('e', $to);

        $this->restrictToClinicAddresses($qb, $clinic);

        return (int) $qb->getQuery()->getSingleScalarResult();
    }

    /**
     * تایم‌استمپ شروع نوبت‌های یک پزشک در بازه — نسخه‌ی لیستی countAppointments
     * برای سری‌های روزانه (یک کوئری به جای یکی به ازای هر روز).
     *
     * @return array<int, int>
     */
    private function appointmentSlotStarts(\App\Doctor\Entity\Doctor $doctor, ?\App\Clinic\Entity\Clinic $clinic, int $from, int $to): array
    {
        $qb = $this->em->createQueryBuilder()
            ->select('a.slotStart')
            ->from(\App\Appointment\Entity\Appointment::class, 'a')
            ->where('a.doctor = :doctor')
            ->andWhere('a.slotStart >= :s AND a.slotStart <= :e')
            ->setParameter('doctor', $doctor)
            ->setParameter('s', $from)
            ->setParameter('e', $to);

        $this->restrictToClinicAddresses($qb, $clinic);

        return array_column($qb->getQuery()->getArrayResult(), 'slotStart');
    }

    private function restrictToClinicAddresses(\Doctrine\ORM\QueryBuilder $qb, ?\App\Clinic\Entity\Clinic $clinic): void
    {
        if ($clinic === null) {
            return;
        }

        $addressIds = array_map(
            fn(\App\Doctor\Entity\DoctorAddress $a): int => (int) $a->getId(),
            $this->addressRepo->findForContext($qb->getParameter('doctor')->getValue(), $clinic->getId())
        );

        $qb->andWhere('a.addressId IN (:addressIds)')
            ->setParameter('addressIds', $addressIds ?: [0]);
    }

    /** فقط محیط شخصی خود پزشک، یا مالک همان کلینیک. */
    private function maySeeFinancials(User $user, ?\App\Clinic\Entity\Clinic $clinic): bool
    {
        if ($clinic === null) {
            return true;
        }

        return $clinic->getUser()->getId() === $user->getId()
            && $this->permChecker->can($user, $clinic, 'payments', 'view');
    }

    // ── Jalali chart helpers ────────────────────────────────────────────────

    /** تقویم شمسی تهران — پایه‌ی همه‌ی محاسبات بازه‌ی نمودارها. */
    private function jalaliCalendar(): \IntlCalendar
    {
        return \IntlCalendar::createInstance(new \DateTimeZone('Asia/Tehran'), 'fa_IR@calendar=persian');
    }

    /**
     * سال/ماه شمسی جاری.
     * @return array{0:int, 1:int} [year, month] با ماه ۱..۱۲
     */
    private function currentJalaliYearMonth(): array
    {
        $cal = $this->jalaliCalendar();

        return [
            $cal->get(\IntlCalendar::FIELD_YEAR),
            $cal->get(\IntlCalendar::FIELD_MONTH) + 1,
        ];
    }

    /**
     * بازه‌ی یونیکس یک ماه شمسی و تعداد روزهای آن.
     * @return array{start:int, end:int, days:int}
     */
    private function jalaliMonthRange(int $year, int $month): array
    {
        $cal = $this->jalaliCalendar();
        $cal->set(\IntlCalendar::FIELD_YEAR, $year);
        $cal->set(\IntlCalendar::FIELD_MONTH, $month - 1);
        $cal->set(\IntlCalendar::FIELD_DAY_OF_MONTH, 1);
        $cal->set(\IntlCalendar::FIELD_HOUR_OF_DAY, 0);
        $cal->set(\IntlCalendar::FIELD_MINUTE, 0);
        $cal->set(\IntlCalendar::FIELD_SECOND, 0);
        $cal->set(\IntlCalendar::FIELD_MILLISECOND, 0);

        $start = (int) ($cal->getTime() / 1000);
        $days  = $cal->getActualMaximum(\IntlCalendar::FIELD_DAY_OF_MONTH);

        return ['start' => $start, 'end' => $start + $days * 86400 - 1, 'days' => $days];
    }

    /**
     * سری تعداد نوبت به تفکیک روزهای یک ماه شمسی (نمودار «تعداد بیماران»).
     * روزهای بدون نوبت صفر می‌مانند تا طول سری برابر طول ماه باشد.
     *
     * `$fetchSlotStarts` باید تایم‌استمپ شروع همه‌ی نوبت‌های بازه را برگرداند —
     * یک کوئری برای کل ماه، نه یکی به ازای هر روز.
     *
     * @param callable(int $from, int $to): array<int, int> $fetchSlotStarts
     * @return array<int, array{label:string, count:int}>
     */
    private function appointmentsByJalaliMonth(int $year, int $month, callable $fetchSlotStarts): array
    {
        $range   = $this->jalaliMonthRange($year, $month);
        $buckets = array_fill(0, $range['days'], 0);
        $dayFmt  = new \IntlDateFormatter(
            'fa_IR@calendar=persian',
            \IntlDateFormatter::NONE,
            \IntlDateFormatter::NONE,
            'Asia/Tehran',
            \IntlDateFormatter::TRADITIONAL,
            'd'
        );

        foreach ($fetchSlotStarts($range['start'], $range['end']) as $slotStart) {
            $day = intdiv($slotStart - $range['start'], 86400);
            if ($day >= 0 && $day < $range['days']) {
                $buckets[$day]++;
            }
        }

        $series = [];
        foreach ($buckets as $day => $count) {
            $series[] = ['label' => $dayFmt->format($range['start'] + $day * 86400), 'count' => $count];
        }

        return $series;
    }

    /**
     * سری درآمد به تفکیک ۱۲ ماه یک سال شمسی (نمودار «میزان درآمد»).
     * @return array<int, array{label:string, amount_rials:int}>
     */
    private function revenueByJalaliYear(string $entityType, int $entityId, int $year): array
    {
        $fmt = new \IntlDateFormatter(
            'fa_IR@calendar=persian',
            \IntlDateFormatter::NONE,
            \IntlDateFormatter::NONE,
            'Asia/Tehran',
            \IntlDateFormatter::TRADITIONAL,
            'MMMM'
        );

        $series = [];
        for ($m = 1; $m <= 12; $m++) {
            $range = $this->jalaliMonthRange($year, $m);
            $series[] = [
                'label'        => $fmt->format($range['start']),
                'amount_rials' => (int) $this->patientSessionRepo->sumRevenue($entityType, $entityId, $range['start'], $range['end']),
            ];
        }

        return $series;
    }

    /**
     * پارامترهای بازه‌ی نمودارها از query string، با fallback به دوره‌ی جاری شمسی.
     * @return array{patients_year:int, patients_month:int, revenue_year:int}
     */
    private function chartPeriodParams(Request $request): array
    {
        [$curYear, $curMonth] = $this->currentJalaliYearMonth();

        $patientsYear  = (int) ($request->query->get('patients_year')  ?: $curYear);
        $patientsMonth = (int) ($request->query->get('patients_month') ?: $curMonth);
        $revenueYear   = (int) ($request->query->get('revenue_year')   ?: $curYear);

        return [
            'patients_year'  => max(1300, min(1500, $patientsYear)),
            'patients_month' => max(1, min(12, $patientsMonth)),
            'revenue_year'   => max(1300, min(1500, $revenueYear)),
        ];
    }

    /**
     * سری ۷ روز اخیر درآمد (بر اساس PatientSession) + پرداختی امروز و هفته.
     * @return array{revenue: array<int, array{label:string, amount_rials:int}>, today_payments_rials:int, week_payments_rials:int}
     */
    private function revenueDaily(string $entityType, int $entityId): array
    {
        $fmt = new \IntlDateFormatter('fa_IR@calendar=persian', \IntlDateFormatter::NONE, \IntlDateFormatter::NONE, 'Asia/Tehran', \IntlDateFormatter::TRADITIONAL, 'd MMMM');
        $series   = [];
        $todayPay = 0;
        $weekPay  = 0;
        for ($i = 6; $i >= 0; $i--) {
            $ds  = strtotime('today midnight') - $i * 86400;
            $de  = $ds + 86399;
            $rev = (int) $this->patientSessionRepo->sumRevenue($entityType, $entityId, $ds, $de);
            $series[] = ['label' => $fmt->format($ds), 'amount_rials' => $rev];
            $weekPay += $rev;
            if ($i === 0) { $todayPay = $rev; }
        }
        return ['revenue' => $series, 'today_payments_rials' => $todayPay, 'week_payments_rials' => $weekPay];
    }

    // ── Secretary Dashboard ──────────────────────────────────────────────────

    #[Route('/api/v1/dashboard/secretary', methods: ['GET'])]
    #[IsGranted('ROLE_SECRETARY')]
    public function secretary(#[CurrentUser] User $user): JsonResponse
    {
        $context = $this->contextResolver->resolve($user);

        if (!$context->isResolved()) {
            return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'دسترسی منشی تنظیم نشده', 403);
        }

        if ($context->isClinic()) {
            return $this->secretaryClinicDashboard($user, $context->clinic);
        }

        $rel = $this->secretaryRepo->findActiveBySecretaryForDoctor($user, $context->doctor);
        if ($rel === null) {
            return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'دسترسی منشی تنظیم نشده', 403);
        }

        return $this->secretaryDoctorDashboard($user, $rel);
    }

    private function secretaryDoctorDashboard(User $user, DoctorSecretary $rel): JsonResponse
    {
        $doctor      = $rel->getDoctor();
        $permissions = $rel->getPermissions();
        $canView     = (bool) ($permissions['resources']['appointments']['view'] ?? false);

        $todayStart = strtotime('today midnight');
        $todayEnd   = strtotime('tomorrow midnight') - 1;
        $tmrStart   = strtotime('tomorrow midnight');
        $tmrEnd     = strtotime('tomorrow midnight') + 86399;

        $todayCount = (int) $this->em->createQuery('
            SELECT COUNT(a.id) FROM App\Appointment\Entity\Appointment a
            WHERE a.doctor = :doctor AND a.slotStart >= :s AND a.slotStart <= :e
        ')->setParameters(['doctor' => $doctor, 's' => $todayStart, 'e' => $todayEnd])
          ->getSingleScalarResult();

        $tmrCount = (int) $this->em->createQuery('
            SELECT COUNT(a.id) FROM App\Appointment\Entity\Appointment a
            WHERE a.doctor = :doctor AND a.slotStart >= :s AND a.slotStart <= :e
        ')->setParameters(['doctor' => $doctor, 's' => $tmrStart, 'e' => $tmrEnd])
          ->getSingleScalarResult();

        $todayAppts = [];
        if ($canView) {
            $todayAppts = $this->em->createQuery('
                SELECT a.uuid, u.realName AS patient_name, u.mobileNumber AS patient_mobile,
                       a.slotStart AS slot_start, a.status
                FROM App\Appointment\Entity\Appointment a
                JOIN a.user u
                WHERE a.doctor = :doctor AND a.slotStart >= :s AND a.slotStart <= :e
                ORDER BY a.slotStart ASC
            ')->setMaxResults(10)->setParameters([
                'doctor' => $doctor,
                's' => $todayStart,
                'e' => $todayEnd,
            ])->getArrayResult();
        }

        return $this->success([
            'scope'  => 'doctor',
            'doctor' => [
                'uuid'   => $doctor->getUuid(),
                'name'   => $doctor->getName(),
                'degree' => $doctor->getDegree(),
            ],
            'permissions'        => $permissions,
            'stats' => [
                'today_appointments'    => $todayCount,
                'tomorrow_appointments' => $tmrCount,
            ],
            'today_appointments' => $todayAppts,
        ]);
    }

    private function secretaryClinicDashboard(User $user, \App\Clinic\Entity\Clinic $clinic): JsonResponse
    {
        $rel = $this->secretaryRepo->findActiveBySecretaryForClinic($user, $clinic);
        if ($rel === null) {
            return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'دسترسی منشی تنظیم نشده', 403);
        }

        $permissions = $rel->getPermissions();
        $canView     = (bool) ($permissions['resources']['appointments']['view'] ?? false);

        $todayStart = strtotime('today midnight');
        $todayEnd   = strtotime('tomorrow midnight') - 1;
        $tmrStart   = strtotime('tomorrow midnight');
        $tmrEnd     = strtotime('tomorrow midnight') + 86399;

        $doctors = $this->secretaryRepo->findDoctorsBySecretaryInClinic($user, $clinic);

        $todayCount = 0;
        $tmrCount   = 0;
        $todayAppts = [];

        if (!empty($doctors)) {
            $todayCount = (int) $this->em->createQuery('
                SELECT COUNT(a.id) FROM App\Appointment\Entity\Appointment a
                WHERE a.doctor IN (:doctors) AND a.slotStart >= :s AND a.slotStart <= :e
            ')->setParameters(['doctors' => $doctors, 's' => $todayStart, 'e' => $todayEnd])
              ->getSingleScalarResult();

            $tmrCount = (int) $this->em->createQuery('
                SELECT COUNT(a.id) FROM App\Appointment\Entity\Appointment a
                WHERE a.doctor IN (:doctors) AND a.slotStart >= :s AND a.slotStart <= :e
            ')->setParameters(['doctors' => $doctors, 's' => $tmrStart, 'e' => $tmrEnd])
              ->getSingleScalarResult();

            if ($canView) {
                $todayAppts = $this->em->createQuery('
                    SELECT a.uuid, u.realName AS patient_name, u.mobileNumber AS patient_mobile,
                           a.slotStart AS slot_start, a.status, d.name AS doctor_name
                    FROM App\Appointment\Entity\Appointment a
                    JOIN a.user u
                    JOIN a.doctor d
                    WHERE a.doctor IN (:doctors) AND a.slotStart >= :s AND a.slotStart <= :e
                    ORDER BY a.slotStart ASC
                ')->setMaxResults(20)->setParameters([
                    'doctors' => $doctors,
                    's' => $todayStart,
                    'e' => $todayEnd,
                ])->getArrayResult();
            }
        }

        return $this->success([
            'scope'  => 'clinic',
            'clinic' => [
                'uuid' => $clinic->getUuid(),
                'name' => $clinic->getName(),
            ],
            'permissions' => $permissions,
            'stats' => [
                'today_appointments'    => $todayCount,
                'tomorrow_appointments' => $tmrCount,
            ],
            'today_appointments' => $todayAppts,
        ]);
    }

    // ── Staff Dashboard ──────────────────────────────────────────────────────

    /**
     * داشبورد پرسنل: سرویس‌هایی که به او تخصیص یافته و نوبت‌های امروزِ خودش.
     *
     * نقش تنها کافی نیست — ردیف فعالِ پرسنل در محیط فعال هم باید وجود داشته باشد،
     * چون توکنِ صادرشده تا انقضا معتبر می‌ماند و غیرفعال‌شدنِ پرسنل باید همان لحظه
     * دسترسی را ببندد.
     */
    #[Route('/api/v1/dashboard/staff', methods: ['GET'])]
    #[IsGranted('ROLE_STAFF')]
    public function staff(#[CurrentUser] User $user): JsonResponse
    {
        $context = $this->contextResolver->resolve($user);
        if (!$context->isResolved()) {
            return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'محیط کاری پرسنل تنظیم نشده', 403);
        }

        [$entityType, $entityId] = $context->toEntityPair();
        $staff = $this->staffRepo->findActiveByUserAndEntity($user, $entityType, $entityId);
        if ($staff === null) {
            return $this->error(ErrorCodes::ERR_FORBIDDEN_001, 'دسترسی پرسنل تنظیم نشده', 403);
        }

        $todayStart = strtotime('today midnight');
        $todayEnd   = strtotime('tomorrow midnight') - 1;

        $todayAppts = $this->em->createQuery('
            SELECT a.uuid, u.realName AS patient_name, u.mobileNumber AS patient_mobile,
                   a.slotStart AS slot_start, a.status
            FROM App\Appointment\Entity\Appointment a
            JOIN a.user u
            WHERE a.staff = :staff AND a.slotStart >= :s AND a.slotStart <= :e
            ORDER BY a.slotStart ASC
        ')->setParameters(['staff' => $staff, 's' => $todayStart, 'e' => $todayEnd])
          ->getArrayResult();

        $services = array_map(
            static fn(ServiceItem $item) => [
                'uuid'             => $item->getUuid(),
                'name'             => $item->getName(),
                'section_name'     => $item->getSection()->getName(),
                'price_rials'      => $item->getPriceRials(),
                'duration_minutes' => $item->getDurationMinutes(),
            ],
            $this->serviceItemRepo->findByStaff($staff),
        );

        return $this->success([
            'scope' => $entityType,
            'staff' => [
                'uuid'      => $staff->getUuid(),
                'full_name' => $staff->getFullName(),
                'job_title' => $staff->getJobTitle(),
            ],
            'owner' => [
                'name' => $context->isClinic()
                    ? ($context->clinic?->getName() ?? '')
                    : ($context->doctor?->getName() ?? ''),
            ],
            'permissions' => StaffPermissions::DEFAULT,
            'stats' => [
                'today_appointments' => count($todayAppts),
                'services'           => count($services),
            ],
            'services'           => $services,
            'today_appointments' => $todayAppts,
        ]);
    }
}

