<?php

namespace App\Tests\Appointment;

use App\Appointment\Entity\Appointment;
use App\ClinicService\Entity\ServiceItem;
use App\ClinicService\Entity\ServiceSection;
use App\Doctor\Entity\Doctor;
use App\Staff\Entity\ClinicStaff;
use App\Tests\ApiTestCase;

/**
 * PATCH /api/v1/appointment/{uuid} — general update used by ویرایش,
 * جا به جایی, انتقال به رزرو and جایگزینی نوبت.
 */
class AppointmentUpdateTest extends ApiTestCase
{
    /** @return array{0: \App\Auth\Entity\User, 1: Doctor, 2: Appointment} */
    private function booking(): array
    {
        $owner  = $this->createUser(['ROLE_DOCTOR']);
        $doctor = new Doctor($owner, 'دکتر');
        $this->em->persist($doctor);
        $this->em->flush();

        $appointment = $this->newAppointment($doctor, $this->createUser(), time() + 86_400, time() + 86_400 + 1_800);
        $this->em->persist($appointment);
        $this->em->flush();

        return [$owner, $doctor, $appointment];
    }

    public function testUpdatesWorkflowFields(): void
    {
        [$owner, $doctor, $appointment] = $this->booking();

        $section = new ServiceSection('doctor', $doctor->getId(), 'زیبایی');
        $item    = new ServiceItem($section, 'لیزر توتال');
        $staff   = new ClinicStaff('doctor', $doctor->getId(), 'سحر ایمانی');
        $this->em->persist($section);
        $this->em->persist($item);
        $this->em->persist($staff);
        $this->em->flush();

        $res = $this->authJson('PATCH', '/api/v1/appointment/' . $appointment->getUuid(), $owner, [
            'service_section_uuid' => $section->getUuid(),
            'service_item_uuid'    => $item->getUuid(),
            'staff_uuid'           => $staff->getUuid(),
            'deposit_required'     => true,
            'deposit_amount_rials' => 5_000_000,
            'note'                 => 'یادداشت',
            'patient_name'         => 'مریم خلیلی',
        ]);
        self::assertSame(200, $this->responseCode());
        $d = $res['data']['data'];
        self::assertSame('زیبایی', $d['service_section']['name']);
        self::assertSame('لیزر توتال', $d['service_item']['name']);
        self::assertSame('سحر ایمانی', $d['staff']['full_name']);
        self::assertTrue($d['deposit_required']);
        self::assertSame('مریم خلیلی', $d['patient_name']);
    }

    public function testRescheduleMovesSlot(): void
    {
        [$owner, , $appointment] = $this->booking();
        $newStart = time() + 2 * 86_400;

        $res = $this->authJson('PATCH', '/api/v1/appointment/' . $appointment->getUuid(), $owner, [
            'slot_start' => $newStart, 'slot_end' => $newStart + 1_800,
        ]);
        self::assertSame(200, $this->responseCode());
        self::assertSame($newStart, $res['data']['data']['slot_start']);
    }

    public function testRescheduleRejectsTakenSlot(): void
    {
        [$owner, $doctor, $appointment] = $this->booking();

        $otherStart = time() + 3 * 86_400;
        $this->em->persist($this->newAppointment($doctor, $this->createUser(), $otherStart, $otherStart + 1_800));
        $this->em->flush();

        $this->authJson('PATCH', '/api/v1/appointment/' . $appointment->getUuid(), $owner, [
            'slot_start' => $otherStart, 'slot_end' => $otherStart + 1_800,
        ]);
        self::assertSame(409, $this->responseCode());
    }

    public function testTransferToReserveAndBack(): void
    {
        [$owner, , $appointment] = $this->booking();
        $day = strtotime('tomorrow midnight');

        $res = $this->authJson('PATCH', '/api/v1/appointment/' . $appointment->getUuid(), $owner, [
            'is_reserve' => true, 'slot_start' => $day, 'slot_end' => $day,
        ]);
        self::assertSame(200, $this->responseCode());
        self::assertTrue($res['data']['data']['is_reserve']);

        $back    = time() + 4 * 86_400;
        $version = $res['data']['data']['version'];
        $res2    = $this->authJson('PATCH', '/api/v1/appointment/' . $appointment->getUuid(), $owner, [
            'is_reserve' => false, 'slot_start' => $back, 'slot_end' => $back + 1_800, 'version' => $version,
        ]);
        self::assertSame(200, $this->responseCode());
        self::assertFalse($res2['data']['data']['is_reserve']);
    }

    public function testRejectsHalfSlotPair(): void
    {
        [$owner, , $appointment] = $this->booking();
        $this->authJson('PATCH', '/api/v1/appointment/' . $appointment->getUuid(), $owner, [
            'slot_start' => time() + 86_400,
        ]);
        self::assertSame(422, $this->responseCode());
    }

    public function testForbiddenForStranger(): void
    {
        [, , $appointment] = $this->booking();
        $stranger = $this->createUser(['ROLE_DOCTOR']);

        $this->authJson('PATCH', '/api/v1/appointment/' . $appointment->getUuid(), $stranger, ['note' => 'x']);
        self::assertSame(403, $this->responseCode());
    }

    public function testUnknownStaffUuidIs422(): void
    {
        [$owner, , $appointment] = $this->booking();
        $this->authJson('PATCH', '/api/v1/appointment/' . $appointment->getUuid(), $owner, [
            'staff_uuid' => 'no-such-uuid',
        ]);
        self::assertSame(422, $this->responseCode());
    }
}
