<?php

namespace App\Tests\PaymentMethod;

use App\Auth\Entity\User;
use App\Auth\Entity\UserActiveContext;
use App\Clinic\Entity\Clinic;
use App\Doctor\Entity\Doctor;
use App\Secretary\Entity\DoctorSecretary;
use App\Shared\Context\EntityContext;
use App\Tests\ApiTestCase;

/**
 * Functional coverage for the per-environment payment methods API
 * (bank accounts + POS devices). Success, error and boundary cases.
 *
 * اسکوپ از فاز ۶ به بعد **محیط** است نه کاربر، پس هر تست به یک محیط واقعی
 * (پزشک یا کلینیک) نیاز دارد؛ کاربری با نقش کلینیک ولی بدون کلینیک، محیطی ندارد.
 */
class PaymentMethodTest extends ApiTestCase
{
    /** کاربری با نقش کلینیک و یک کلینیک واقعی — یعنی محیط دارد. */
    private function clinicOwner(): User
    {
        $owner  = $this->createUser(['ROLE_CLINIC']);
        $clinic = new Clinic($owner);
        $clinic->setName('کلینیک روش پرداخت');
        $this->em->persist($clinic);
        $this->em->flush();

        return $owner;
    }

    private function doctorOwner(): User
    {
        $user = $this->createUser(['ROLE_DOCTOR']);
        $this->em->persist(new Doctor($user, 'دکتر روش پرداخت'));
        $this->em->flush();

        return $user;
    }

    // ---- Bank accounts -----------------------------------------------------

    public function testEmptyBankAccountListForNewClinic(): void
    {
        $res = $this->authJson('GET', '/api/v1/my/payment-methods/bank-accounts', $this->clinicOwner());

        $this->assertSame(200, $this->responseCode());
        $this->assertTrue($res['success']);
        $this->assertSame([], $res['data']);
    }

    public function testCreateAndListBankAccount(): void
    {
        $user = $this->clinicOwner();

        $created = $this->authJson('POST', '/api/v1/my/payment-methods/bank-accounts', $user, [
            'bank_name'      => 'ملی',
            'card_number'    => '6037991234567890',
            'account_number' => '0101234567890',
            'shaba_number'   => 'IR820540102680020817909002',
        ]);

        $this->assertSame(201, $this->responseCode());
        $this->assertSame('ملی', $created['data']['bank_name']);
        $this->assertTrue($created['data']['is_active']);
        $this->assertNotEmpty($created['data']['uuid']);
        $this->assertSame('clinic', $created['data']['entity_type'], 'حساب باید به محیط فعال بچسبد');

        $list = $this->authJson('GET', '/api/v1/my/payment-methods/bank-accounts', $user);
        $this->assertCount(1, $list['data']);
        $this->assertSame('0101234567890', $list['data'][0]['account_number']);
    }

    public function testCreateBankAccountValidationErrorWhenBankNameMissing(): void
    {
        $res = $this->authJson('POST', '/api/v1/my/payment-methods/bank-accounts', $this->clinicOwner(), [
            'account_number' => '0101234567890',
        ]);

        $this->assertSame(422, $this->responseCode());
        $this->assertFalse($res['success']);
        $this->assertSame('bank_name', $res['errors'][0]['field']);
    }

    public function testUpdateBankAccount(): void
    {
        $user    = $this->clinicOwner();
        $created = $this->authJson('POST', '/api/v1/my/payment-methods/bank-accounts', $user, [
            'bank_name'      => 'ملی',
            'account_number' => '0101234567890',
        ]);
        $uuid = $created['data']['uuid'];

        $updated = $this->authJson('PUT', "/api/v1/my/payment-methods/bank-accounts/$uuid", $user, [
            'bank_name'      => 'ملت',
            'account_number' => '0209876543210',
        ]);

        $this->assertSame(200, $this->responseCode());
        $this->assertSame('ملت', $updated['data']['bank_name']);
        $this->assertSame('0209876543210', $updated['data']['account_number']);
    }

    public function testToggleBankAccountStatus(): void
    {
        $user    = $this->clinicOwner();
        $created = $this->authJson('POST', '/api/v1/my/payment-methods/bank-accounts', $user, [
            'bank_name'      => 'ملی',
            'account_number' => '0101234567890',
        ]);
        $uuid = $created['data']['uuid'];
        $this->assertTrue($created['data']['is_active']);

        $toggled = $this->authJson('PATCH', "/api/v1/my/payment-methods/bank-accounts/$uuid/status", $user);

        $this->assertSame(200, $this->responseCode());
        $this->assertFalse($toggled['data']['is_active']);
    }

    public function testToggleUnknownBankAccountReturns404(): void
    {
        $res = $this->authJson(
            'PATCH',
            '/api/v1/my/payment-methods/bank-accounts/does-not-exist/status',
            $this->clinicOwner(),
        );

        $this->assertSame(404, $this->responseCode());
        $this->assertFalse($res['success']);
    }

    public function testCannotTouchAnotherClinicsBankAccount(): void
    {
        $owner   = $this->clinicOwner();
        $other   = $this->clinicOwner();
        $created = $this->authJson('POST', '/api/v1/my/payment-methods/bank-accounts', $owner, [
            'bank_name'      => 'ملی',
            'account_number' => '0101234567890',
        ]);
        $uuid = $created['data']['uuid'];

        $res = $this->authJson('PATCH', "/api/v1/my/payment-methods/bank-accounts/$uuid/status", $other);

        $this->assertSame(404, $this->responseCode());
        $this->assertFalse($res['success']);
    }

    public function testBankAccountForbiddenForPlainUser(): void
    {
        $this->authJson('GET', '/api/v1/my/payment-methods/bank-accounts', $this->createUser(['ROLE_USER']));

        $this->assertSame(403, $this->responseCode());
    }

    /** ❌ نقشِ کلینیک بدون کلینیکِ واقعی محیطی ندارد، پس کارتی هم ندارد. */
    public function testRoleWithoutAnActualEnvironmentIsRejected(): void
    {
        $this->authJson('GET', '/api/v1/my/payment-methods/bank-accounts', $this->createUser(['ROLE_CLINIC']));

        $this->assertSame(403, $this->responseCode());
    }

    // ---- POS devices -------------------------------------------------------

    public function testCreateAndListPos(): void
    {
        $user = $this->doctorOwner();

        $created = $this->authJson('POST', '/api/v1/my/payment-methods/pos', $user, [
            'bank_name'       => 'ملت',
            'serial_number'   => 'SN-98765',
            'terminal_number' => '123456',
        ]);

        $this->assertSame(201, $this->responseCode());
        $this->assertSame('ملت', $created['data']['bank_name']);
        $this->assertSame('123456', $created['data']['terminal_number']);
        $this->assertTrue($created['data']['is_active']);
        $this->assertSame('doctor', $created['data']['entity_type']);

        $list = $this->authJson('GET', '/api/v1/my/payment-methods/pos', $user);
        $this->assertCount(1, $list['data']);
    }

    public function testCreatePosValidationErrorWhenTerminalMissing(): void
    {
        $res = $this->authJson('POST', '/api/v1/my/payment-methods/pos', $this->doctorOwner(), [
            'bank_name' => 'ملت',
        ]);

        $this->assertSame(422, $this->responseCode());
        $this->assertSame('terminal_number', $res['errors'][0]['field']);
    }

    public function testTogglePosStatus(): void
    {
        // منشی به روش‌های پرداخت فقط با مجوز payments از طریق رابطهٔ فعال دسترسی دارد.
        $user    = $this->createSecretaryWithPayments(['view' => true, 'create' => true, 'update' => true]);
        $created = $this->authJson('POST', '/api/v1/my/payment-methods/pos', $user, [
            'bank_name'       => 'تجارت',
            'terminal_number' => '345678',
        ]);
        $uuid = $created['data']['uuid'];

        $toggled = $this->authJson('PATCH', "/api/v1/my/payment-methods/pos/$uuid/status", $user);

        $this->assertSame(200, $this->responseCode());
        $this->assertFalse($toggled['data']['is_active']);
    }

    public function testSecretaryWithoutPaymentsPermissionCannotManagePos(): void
    {
        $user = $this->createSecretaryWithPayments(['view' => false, 'create' => false, 'update' => false]);

        $this->authJson('POST', '/api/v1/my/payment-methods/pos', $user, [
            'bank_name'       => 'تجارت',
            'terminal_number' => '345678',
        ]);

        $this->assertSame(403, $this->responseCode());
    }

    /** منشی با رابطهٔ فعالِ کلینیک + context + مجوز payments مشخص. */
    private function createSecretaryWithPayments(array $payments): User
    {
        $owner  = $this->createUser(['ROLE_CLINIC']);
        $clinic = new Clinic($owner);
        $this->em->persist($clinic);

        $doctor = new Doctor($this->createUser(['ROLE_DOCTOR']), 'دکتر تست');
        $this->em->persist($doctor);
        $clinic->getDoctors()->add($doctor);

        $secretary = $this->createUser(['ROLE_SECRETARY']);
        $rel       = new DoctorSecretary($doctor, $secretary, $clinic);
        $rel->mergePermissions(['resources' => ['payments' => $payments]]);
        $this->em->persist($rel);
        $this->em->persist(new UserActiveContext($secretary, $clinic->getUuid(), EntityContext::TYPE_CLINIC));
        $this->em->flush();

        return $secretary;
    }

    public function testPosListIsolatedPerEnvironment(): void
    {
        $a = $this->clinicOwner();
        $b = $this->clinicOwner();
        $this->authJson('POST', '/api/v1/my/payment-methods/pos', $a, [
            'bank_name'       => 'صادرات',
            'terminal_number' => '901234',
        ]);

        $listB = $this->authJson('GET', '/api/v1/my/payment-methods/pos', $b);

        $this->assertSame([], $listB['data']);
    }
}
