<?php

namespace App\Tests\Patient;

use App\Doctor\Entity\Doctor;
use App\Patient\Entity\PatientRecord;
use App\Patient\Entity\PatientSession;
use App\Tag\Entity\TenantTag;
use App\Tests\ApiTestCase;
use App\UserProfile\Entity\UserProfile;

/**
 * GET /api/v1/patients advanced filters: tags, gender, insurance, admission
 * date range, service status (pending/completed) and has-debt.
 */
class PatientListFilterTest extends ApiTestCase
{
    private function doctor(): array
    {
        $owner  = $this->createUser(['ROLE_DOCTOR']);
        $doctor = new Doctor($owner, 'دکتر تست');
        $this->em->persist($doctor);
        $this->em->flush();

        return [$owner, $doctor];
    }

    /**
     * @param array{gender?:string,insurance?:int,tag?:TenantTag,pending?:bool,paid?:bool,createdAt?:int} $opts
     */
    private function record(Doctor $doctor, array $opts = []): PatientRecord
    {
        $patient = $this->createUser(['ROLE_USER']);
        $record  = new PatientRecord('doctor', $doctor->getId(), $patient, 'doctor', $doctor->getId());
        if (isset($opts['createdAt'])) {
            $ref = new \ReflectionProperty($record, 'createdAt');
            $ref->setAccessible(true);
            $ref->setValue($record, $opts['createdAt']);
        }
        $this->em->persist($record);

        if (isset($opts['gender']) || isset($opts['insurance'])) {
            $profile = new UserProfile($patient);
            if (isset($opts['gender']))    $profile->setGender($opts['gender']);
            if (isset($opts['insurance'])) $profile->setBasicInsuranceId($opts['insurance']);
            $this->em->persist($profile);
        }
        if (isset($opts['tag'])) {
            $record->getTags()->add($opts['tag']);
        }
        if (!empty($opts['pending']) || !empty($opts['paid'])) {
            $session = new PatientSession($record);
            $session->setPaymentMethod(!empty($opts['pending']) ? 'pending' : 'cash');
            $this->em->persist($session);
        }
        $this->em->flush();

        return $record;
    }

    private function tag(Doctor $doctor, string $name): TenantTag
    {
        $tag = new TenantTag('doctor', $doctor->getId(), $name, '#5559CE');
        $this->em->persist($tag);
        $this->em->flush();
        return $tag;
    }

    private function uuids(array $res): array
    {
        return array_map(static fn(array $r) => $r['uuid'], $res['data']);
    }

    public function testFilterByTag(): void
    {
        [$owner, $doctor] = $this->doctor();
        $tag     = $this->tag($doctor, 'خوش‌حساب');
        $tagged  = $this->record($doctor, ['tag' => $tag]);
        $this->record($doctor); // untagged

        $res = $this->authJson('GET', '/api/v1/patients?tags=' . $tag->getUuid(), $owner);
        self::assertSame(200, $this->responseCode());
        self::assertSame(1, $res['meta']['totalRecords']);
        self::assertSame($tagged->getUuid(), $res['data'][0]['uuid']);
    }

    public function testFilterByGenderAndInsurance(): void
    {
        [$owner, $doctor] = $this->doctor();
        $male   = $this->record($doctor, ['gender' => 'male', 'insurance' => 7]);
        $this->record($doctor, ['gender' => 'female', 'insurance' => 9]);

        $byGender = $this->authJson('GET', '/api/v1/patients?gender=male', $owner);
        self::assertSame(1, $byGender['meta']['totalRecords']);
        self::assertSame($male->getUuid(), $byGender['data'][0]['uuid']);

        $byIns = $this->authJson('GET', '/api/v1/patients?insurance_id=7', $owner);
        self::assertSame(1, $byIns['meta']['totalRecords']);
        self::assertSame($male->getUuid(), $byIns['data'][0]['uuid']);
    }

    public function testFilterByAdmissionDateRange(): void
    {
        [$owner, $doctor] = $this->doctor();
        $old = $this->record($doctor, ['createdAt' => 1000]);
        $new = $this->record($doctor, ['createdAt' => 5000]);

        $res = $this->authJson('GET', '/api/v1/patients?admitted_from=4000&admitted_to=6000', $owner);
        self::assertSame(1, $res['meta']['totalRecords']);
        self::assertSame($new->getUuid(), $res['data'][0]['uuid']);
        self::assertNotContains($old->getUuid(), $this->uuids($res));
    }

    public function testFilterByServiceStatusAndDebt(): void
    {
        [$owner, $doctor] = $this->doctor();
        $pending   = $this->record($doctor, ['pending' => true]);
        $completed = $this->record($doctor, ['paid' => true]);

        $onlyPending = $this->authJson('GET', '/api/v1/patients?service_status=pending', $owner);
        self::assertSame(1, $onlyPending['meta']['totalRecords']);
        self::assertSame($pending->getUuid(), $onlyPending['data'][0]['uuid']);

        $onlyCompleted = $this->authJson('GET', '/api/v1/patients?service_status=completed', $owner);
        self::assertSame(1, $onlyCompleted['meta']['totalRecords']);
        self::assertSame($completed->getUuid(), $onlyCompleted['data'][0]['uuid']);

        $withDebt = $this->authJson('GET', '/api/v1/patients?has_debt=1', $owner);
        self::assertSame(1, $withDebt['meta']['totalRecords']);
        self::assertSame($pending->getUuid(), $withDebt['data'][0]['uuid']);
    }

    public function testNoFiltersReturnsAll(): void
    {
        [$owner, $doctor] = $this->doctor();
        $this->record($doctor);
        $this->record($doctor);

        $res = $this->authJson('GET', '/api/v1/patients', $owner);
        self::assertSame(200, $this->responseCode());
        self::assertSame(2, $res['meta']['totalRecords']);
    }
}
