<?php

namespace App\Appointment\Controller;

use App\Appointment\Entity\DateOverride;
use App\Appointment\Entity\Holiday;
use App\Appointment\Entity\WeeklySchedule;
use App\Appointment\Repository\DateOverrideRepository;
use App\Appointment\Repository\HolidayRepository;
use App\Appointment\Repository\WeeklyScheduleRepository;
use App\Auth\Entity\User;
use App\Clinic\Entity\Clinic;
use App\Clinic\Repository\ClinicRepository;
use App\Doctor\Entity\DoctorAddress;
use App\Doctor\Repository\DoctorAddressRepository;
use App\Doctor\Repository\DoctorRepository;
use App\Shared\Constant\ErrorCodes;
use App\Shared\Controller\BaseController;
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: 'Appointment Settings')]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
class AppointmentSettingsController extends BaseController
{
    public function __construct(
        private readonly WeeklyScheduleRepository $scheduleRepo,
        private readonly DateOverrideRepository   $overrideRepo,
        private readonly HolidayRepository        $holidayRepo,
        private readonly DoctorRepository         $doctorRepo,
        private readonly DoctorAddressRepository  $addressRepo,
        private readonly ClinicRepository         $clinicRepo,
    ) {}

    // ── Weekly Schedule ───────────────────────────────────────────────────────

    #[Route('/api/v1/appointment-settings/weekly-schedule', methods: ['POST'])]
    public function createSchedule(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $data       = json_decode($request->getContent(), true) ?? [];
        $doctorUuid = trim($data['doctor_uuid'] ?? '');

        $doctor = $this->doctorRepo->findByUuid($doctorUuid);
        if ($doctor === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
        }

        if ($doctor->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        if (($err = $this->validateSessionsHaveLocation($data['schedule'] ?? [])) !== null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, $err, 422);
        }

        // Only one schedule per doctor — upsert
        $schedule = $this->scheduleRepo->findByDoctor($doctor);
        if ($schedule !== null) {
            $schedule->setSetting($data['schedule'] ?? []);
        } else {
            $schedule = new WeeklySchedule($doctor, $data['schedule'] ?? []);
        }

        if (isset($data['meta']) && is_array($data['meta'])) {
            $schedule->setMeta($data['meta']);
        }

        $this->scheduleRepo->save($schedule);

        return $this->success(['data' => $schedule->toArray()], 201);
    }

    #[Route('/api/v1/appointment-settings/weekly-schedule/{uuid}', methods: ['PATCH'])]
    public function updateSchedule(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
    {
        // uuid may be doctor uuid or schedule uuid
        $schedule = $this->scheduleRepo->findByUuid($uuid);
        if ($schedule === null) {
            $doctor   = $this->doctorRepo->findByUuid($uuid);
            $schedule = $doctor ? $this->scheduleRepo->findByDoctor($doctor) : null;
        }

        if ($schedule === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'برنامه یافت نشد', 404);
        }

        if ($schedule->getDoctor()->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $data = json_decode($request->getContent(), true) ?? [];
        if (isset($data['schedule'])) {
            if (($err = $this->validateSessionsHaveLocation($data['schedule'])) !== null) {
                return $this->error(ErrorCodes::ERR_VALIDATION_001, $err, 422);
            }
            $schedule->setSetting($data['schedule']);
        }
        if (isset($data['meta']) && is_array($data['meta'])) {
            $schedule->setMeta($data['meta']);
        }

        $this->scheduleRepo->save($schedule);

        return $this->success(['data' => $schedule->toArray()]);
    }

    #[Route('/api/v1/appointment-settings/weekly-schedule/{uuid}', methods: ['GET'])]
    public function getSchedule(string $uuid, #[CurrentUser] User $user): JsonResponse
    {
        // Try doctor uuid first, then schedule uuid
        $doctor   = $this->doctorRepo->findByUuid($uuid);
        $schedule = $doctor
            ? $this->scheduleRepo->findByDoctor($doctor)
            : $this->scheduleRepo->findByUuid($uuid);

        if ($schedule === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'برنامه یافت نشد', 404);
        }

        if ($schedule->getDoctor()->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        return $this->success(['data' => $schedule->toArray()]);
    }

    #[Route('/api/v1/booking-setting/{uuid}', methods: ['DELETE'])]
    public function deleteSchedule(string $uuid, #[CurrentUser] User $user): JsonResponse
    {
        $schedule = $this->scheduleRepo->findByUuid($uuid);
        if ($schedule === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'برنامه یافت نشد', 404);
        }

        if ($schedule->getDoctor()->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $this->scheduleRepo->remove($schedule);

        return $this->success(['message' => 'برنامه هفتگی با موفقیت حذف شد']);
    }

    // ── Date Overrides ────────────────────────────────────────────────────────

    #[Route('/api/v1/appointment-settings/date-override/list/{doctorUuid}', methods: ['GET'])]
    public function listOverrides(string $doctorUuid, #[CurrentUser] User $user): JsonResponse
    {
        $doctor = $this->doctorRepo->findByUuid($doctorUuid);
        if ($doctor === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
        }

        if ($doctor->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $overrides = array_map(
            fn(DateOverride $o) => $o->toArray(),
            $this->overrideRepo->findByDoctor($doctor)
        );

        return $this->success(['data' => $overrides]);
    }

    #[Route('/api/v1/appointment-settings/date-override', methods: ['POST'])]
    public function createOverride(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $data       = json_decode($request->getContent(), true) ?? [];
        $doctorUuid = trim($data['doctor_uuid'] ?? '');
        $dateStr    = trim($data['date'] ?? '');

        $doctor = $this->doctorRepo->findByUuid($doctorUuid);
        if ($doctor === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
        }

        if ($doctor->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $timestamp = strtotime($dateStr);
        if ($timestamp === false || $timestamp === -1) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'فرمت تاریخ نادرست است', 422, 'date');
        }

        $override = new DateOverride($doctor, $timestamp, (bool) ($data['active'] ?? false));
        if (isset($data['reason']))       $override->setReason($data['reason']);
        if (isset($data['custom_slots'])) $override->setSetting($data['custom_slots']);

        $this->overrideRepo->save($override);

        return $this->success(['data' => $override->toArray()], 201);
    }

    #[Route('/api/v1/appointment-settings/date-override/{uuid}', methods: ['PATCH'])]
    public function updateOverride(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $override = $this->overrideRepo->findByUuid($uuid);
        if ($override === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'Override یافت نشد', 404);
        }

        if ($override->getDoctor()->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $data = json_decode($request->getContent(), true) ?? [];
        if (array_key_exists('active', $data))       $override->setActive((bool) $data['active']);
        if (array_key_exists('reason', $data))       $override->setReason($data['reason']);
        if (array_key_exists('custom_slots', $data)) $override->setSetting($data['custom_slots']);
        if (!empty($data['date'])) {
            $ts = strtotime($data['date']);
            if ($ts !== false) $override->setDate($ts);
        }

        $this->overrideRepo->save($override);

        return $this->success(['data' => $override->toArray()]);
    }

    #[Route('/api/v1/appointment-settings/date-override/{uuid}', methods: ['DELETE'])]
    public function deleteOverride(string $uuid, #[CurrentUser] User $user): JsonResponse
    {
        $override = $this->overrideRepo->findByUuid($uuid);
        if ($override === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'Override یافت نشد', 404);
        }

        if ($override->getDoctor()->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $this->overrideRepo->remove($override);

        return $this->success(['message' => 'Override با موفقیت حذف شد']);
    }

    #[Route('/api/v1/appointment-settings/date-override/{uuid}', methods: ['GET'])]
    public function getOverride(string $uuid, #[CurrentUser] User $user): JsonResponse
    {
        $override = $this->overrideRepo->findByUuid($uuid);
        if ($override === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'Override یافت نشد', 404);
        }

        if ($override->getDoctor()->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        return $this->success(['data' => $override->toArray()]);
    }

    // ── Holidays ──────────────────────────────────────────────────────────────

    #[Route('/api/v1/appointment-settings/holidays/list/{doctorUuid}', methods: ['GET'])]
    public function listHolidays(string $doctorUuid, #[CurrentUser] User $user): JsonResponse
    {
        $doctor = $this->doctorRepo->findByUuid($doctorUuid);
        if ($doctor === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
        }

        if ($doctor->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $items = array_map(fn(Holiday $h) => $h->toArray(), $this->holidayRepo->findAllByDoctor($doctor));

        return $this->success(['data' => $items]);
    }

    #[Route('/api/v1/appointment-settings/holidays/{uuid}', methods: ['DELETE'])]
    public function deleteHoliday(string $uuid, #[CurrentUser] User $user): JsonResponse
    {
        $holiday = $this->holidayRepo->findByUuid($uuid);
        if ($holiday === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'تعطیلات یافت نشد', 404);
        }

        if ($holiday->getDoctor()->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $this->holidayRepo->remove($holiday);

        return $this->success(['message' => 'تعطیلات حذف شد']);
    }

    #[Route('/api/v1/appointment-settings/holidays', methods: ['POST'])]
    public function createHoliday(Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $data       = json_decode($request->getContent(), true) ?? [];
        $doctorUuid = trim($data['doctor_uuid'] ?? '');
        $startStr   = trim($data['start_date'] ?? '');
        $endStr     = trim($data['end_date'] ?? '');

        $doctor = $this->doctorRepo->findByUuid($doctorUuid);
        if ($doctor === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
        }

        if ($doctor->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $startTs = strtotime($startStr);
        $endTs   = strtotime($endStr);

        if (!$startTs || !$endTs || $endTs < $startTs) {
            return $this->error(ErrorCodes::ERR_VALIDATION_001, 'تاریخ نادرست است', 422);
        }

        $holiday = new Holiday($doctor, $startTs, $endTs);
        if (isset($data['reason'])) $holiday->setReason($data['reason']);

        $this->holidayRepo->save($holiday);

        return $this->success(['data' => $holiday->toArray()], 201);
    }

    #[Route('/api/v1/appointment-settings/holidays/{uuid}', methods: ['PATCH'])]
    public function updateHoliday(string $uuid, Request $request, #[CurrentUser] User $user): JsonResponse
    {
        $holiday = $this->holidayRepo->findByUuid($uuid);
        if ($holiday === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'تعطیلات یافت نشد', 404);
        }

        if ($holiday->getDoctor()->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $data = json_decode($request->getContent(), true) ?? [];
        if (array_key_exists('active', $data))  $holiday->setActive((bool) $data['active']);
        if (array_key_exists('reason', $data))  $holiday->setReason($data['reason']);
        if (!empty($data['start_date'])) {
            $ts = strtotime($data['start_date']);
            if ($ts) $holiday->setStartDate($ts);
        }
        if (!empty($data['end_date'])) {
            $ts = strtotime($data['end_date']);
            if ($ts) $holiday->setEndDate($ts);
        }

        $this->holidayRepo->save($holiday);

        return $this->success(['data' => $holiday->toArray()]);
    }

    // ── Available Locations ───────────────────────────────────────────────────

    #[Route('/api/v1/appointment-settings/available-locations/{doctorUuid}', methods: ['GET'])]
    public function availableLocations(string $doctorUuid, #[CurrentUser] User $user): JsonResponse
    {
        $doctor = $this->doctorRepo->findByUuid($doctorUuid);
        if ($doctor === null) {
            return $this->error(ErrorCodes::ERR_VALIDATION_002, 'دکتر یافت نشد', 404);
        }

        if ($doctor->getUser()->getId() !== $user->getId() && !$user->hasRole('ROLE_ADMIN')) {
            return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403);
        }

        $clinics    = $this->clinicRepo->findByDoctor($doctor);
        $clinicIds  = array_map(fn(Clinic $c) => $c->getId(), $clinics);
        $clinicMap  = [];
        foreach ($clinics as $clinic) {
            $clinicMap[$clinic->getId()] = $clinic->getName();
        }

        $addresses = $this->addressRepo->findAvailableForDoctor($doctor, $clinicIds);

        $result = array_map(function (DoctorAddress $a) use ($clinicMap): array {
            $data               = $a->toArray();
            $data['clinic_name'] = $a->getClinicId() !== null ? ($clinicMap[$a->getClinicId()] ?? null) : null;
            return $data;
        }, $addresses);

        return $this->success(['data' => $result]);
    }

    /**
     * هر session فعال در برنامه‌ی هفتگی باید آدرس (location_id) داشته باشد.
     * در صورت نقص، پیام خطا برمی‌گرداند؛ در غیر این صورت null.
     */
    private function validateSessionsHaveLocation(array $schedule): ?string
    {
        foreach ($schedule as $day) {
            foreach (($day['sessions'] ?? []) as $session) {
                if (($session['active'] ?? false) && empty($session['location_id'])) {
                    return 'برای هر شیفت فعال باید آدرس (مطب/کلینیک) انتخاب شود';
                }
            }
        }
        return null;
    }
}
