<?php

namespace App\Tests\Patient;

use App\Doctor\Entity\Doctor;
use App\Patient\Entity\PatientRecord;
use App\Patient\Entity\PatientSession;
use App\Settlement\Entity\WalletTransaction;
use App\Tests\ApiTestCase;

/**
 * تسویهٔ یک مراجعه (PatientSession) از کیف پول: PATCH /session/{uuid} با
 * payment_method=wallet مبلغِ نهایی را به‌صورت debit از موجودی کسر می‌کند و در
 * صورتِ ناکافی بودن موجودی رد می‌شود.
 */
class PatientWalletSessionSettleTest extends ApiTestCase
{
    /** @return array{0: \App\Auth\Entity\User, 1: PatientRecord, 2: \App\Auth\Entity\User} */
    private function recordFor(): array
    {
        $owner  = $this->createUser(['ROLE_DOCTOR']);
        $doctor = new Doctor($owner, 'دکتر');
        $this->em->persist($doctor);
        $this->em->flush();

        $patient = $this->createUser(['ROLE_USER']);
        $record  = new PatientRecord('doctor', $doctor->getId(), $patient, 'doctor', $doctor->getId());
        $this->em->persist($record);
        $this->em->flush();

        return [$owner, $record, $patient];
    }

    private function sessionFor(PatientRecord $record, int $finalPriceRials): PatientSession
    {
        $session = new PatientSession($record);
        $session->setFinalPriceRials($finalPriceRials);
        $this->em->persist($session);
        $this->em->flush();

        return $session;
    }

    public function testSettleSessionFromWalletDebitsBalanceAndMarksPaid(): void
    {
        [$owner, $record, $patient] = $this->recordFor();
        $this->em->persist(new WalletTransaction($patient, 1_000_000, 'credit', 1_000_000));
        $this->em->flush();

        $session = $this->sessionFor($record, 400_000);

        $res = $this->authJson('PATCH', '/api/v1/session/' . $session->getUuid(), $owner, [
            'payment_method' => 'wallet',
        ]);
        self::assertSame(200, $this->responseCode());
        self::assertSame('wallet', $res['data']['payment_method']);
        self::assertTrue($res['data']['is_paid']);

        // موجودی از ۱٬۰۰۰٬۰۰۰ به ۶۰۰٬۰۰۰ رسید و یک تراکنشِ debitِ گره‌خورده به مراجعه ثبت شد.
        $wallet = $this->authJson('GET', '/api/v1/patient/' . $record->getUuid() . '/wallet', $owner);
        self::assertSame(600_000, $wallet['data']['balance_rials']);
        $debit = $wallet['data']['recent_transactions'][0];
        self::assertSame('debit', $debit['type']);
        self::assertSame('wallet', $debit['payment_method']);
        self::assertSame('session:' . $session->getUuid(), $debit['reference']);
        self::assertSame($owner->getMobileNumber(), $debit['created_by_name']);
    }

    public function testSettleSessionFromWalletRejectedWhenInsufficient(): void
    {
        [$owner, $record, $patient] = $this->recordFor();
        $this->em->persist(new WalletTransaction($patient, 100_000, 'credit', 100_000));
        $this->em->flush();

        $session = $this->sessionFor($record, 400_000);

        $res = $this->authJson('PATCH', '/api/v1/session/' . $session->getUuid(), $owner, [
            'payment_method' => 'wallet',
        ]);
        self::assertSame(422, $this->responseCode());
        self::assertSame('ERR_WALLET_INSUFFICIENT', $res['errors'][0]['code']);

        // مراجعه تسویه نشد و موجودی دست‌نخورده ماند.
        $wallet = $this->authJson('GET', '/api/v1/patient/' . $record->getUuid() . '/wallet', $owner);
        self::assertSame(100_000, $wallet['data']['balance_rials']);
    }

    /**
     * رگرسیون: تسویهٔ کیف پول باید SessionPayment ثبت کند تا مراجعه واقعاً is_paid شود.
     * پیش‌تر فقط موجودی کسر می‌شد و مراجعه پرداخت‌نشده می‌ماند، پس PATCH دوم دوباره
     * از کیف پول کم می‌کرد (کسر مضاعف از پول واقعی بیمار).
     */
    public function testSettlingTwiceDoesNotChargeWalletAgain(): void
    {
        [$owner, $record, $patient] = $this->recordFor();
        $this->em->persist(new WalletTransaction($patient, 1_000_000, 'credit', 1_000_000));
        $this->em->flush();

        $session = $this->sessionFor($record, 400_000);
        $url     = '/api/v1/session/' . $session->getUuid();

        $this->authJson('PATCH', $url, $owner, ['payment_method' => 'wallet']);
        self::assertSame(200, $this->responseCode());

        // تسویهٔ دوباره روی همان مراجعه نباید چیزی کسر کند.
        $res = $this->authJson('PATCH', $url, $owner, ['payment_method' => 'wallet']);
        self::assertSame(200, $this->responseCode());
        self::assertTrue($res['data']['is_paid']);

        $wallet = $this->authJson('GET', '/api/v1/patient/' . $record->getUuid() . '/wallet', $owner);
        self::assertSame(600_000, $wallet['data']['balance_rials']);
    }

    /**
     * رگرسیون: مبلغِ کسرشده باید مانده (پس از تخفیف) باشد نه قیمتِ نهاییِ خام.
     */
    public function testWalletSettleChargesPayableAfterDiscount(): void
    {
        [$owner, $record, $patient] = $this->recordFor();
        $this->em->persist(new WalletTransaction($patient, 1_000_000, 'credit', 1_000_000));
        $this->em->flush();

        $session = $this->sessionFor($record, 400_000);
        $session->setDiscount('fixed', 100_000, 100_000);
        $this->em->flush();

        $res = $this->authJson('PATCH', '/api/v1/session/' . $session->getUuid(), $owner, [
            'payment_method' => 'wallet',
        ]);
        self::assertSame(200, $this->responseCode());
        self::assertTrue($res['data']['is_paid']);

        // ۴۰۰٬۰۰۰ − ۱۰۰٬۰۰۰ تخفیف = ۳۰۰٬۰۰۰ کسر شود، نه ۴۰۰٬۰۰۰.
        $wallet = $this->authJson('GET', '/api/v1/patient/' . $record->getUuid() . '/wallet', $owner);
        self::assertSame(700_000, $wallet['data']['balance_rials']);
    }

    public function testSettleSessionWithCashDoesNotTouchWallet(): void
    {
        [$owner, $record, $patient] = $this->recordFor();
        $this->em->persist(new WalletTransaction($patient, 500_000, 'credit', 500_000));
        $this->em->flush();

        $session = $this->sessionFor($record, 300_000);

        $this->authJson('PATCH', '/api/v1/session/' . $session->getUuid(), $owner, ['payment_method' => 'cash']);
        self::assertSame(200, $this->responseCode());

        $wallet = $this->authJson('GET', '/api/v1/patient/' . $record->getUuid() . '/wallet', $owner);
        self::assertSame(500_000, $wallet['data']['balance_rials']);
    }
}
