<?php

namespace App\Tests\Secretary;

use App\Appointment\Entity\Appointment;
use App\Auth\Entity\User;
use App\Clinic\Entity\Clinic;
use App\Config\Repository\SiteConfigRepository;
use App\Doctor\Entity\Doctor;
use App\Payment\Entity\Payment;
use App\Secretary\Entity\DoctorSecretary;
use App\Secretary\Repository\SecretaryEarningRepository;
use App\Settlement\Repository\FinancialBreakdownRepository;
use App\Settlement\Repository\SettlementRepository;
use App\Settlement\Service\CommissionService;
use App\Tests\ApiTestCase;

/**
 * سهم منشی از نوبت آنلاین: درصدِ رابطهٔ منشی روی «خالصِ پس از مالیات» — همان مبنایی
 * که پورسانت نماینده از آن گرفته می‌شود — و مستقل از وجود نماینده.
 */
class SecretaryOnlineShareTest extends ApiTestCase
{
    private const GROSS = 10_000_000;

    protected function setUp(): void
    {
        parent::setUp();
        $config = static::getContainer()->get(SiteConfigRepository::class);
        // کسورات را قطعی می‌کنیم تا انتظارِ عددی تست پایدار بماند.
        $config->set('sms_panel_fee_rials', '0');
        $config->set('tax_enabled', '0');
        $config->set('tax_percent', '0');
    }

    private function makeDoctor(): Doctor
    {
        $user   = $this->createUser(['ROLE_USER', 'ROLE_DOCTOR']);
        $doctor = new Doctor($user, 'دکتر تست سهم منشی');
        $this->em->persist($doctor);
        $this->em->flush();

        return $doctor;
    }

    private function makeSecretary(Doctor $doctor, float $percent, bool $enabled = true, ?Clinic $clinic = null): DoctorSecretary
    {
        $user     = $this->createUser(['ROLE_USER', 'ROLE_SECRETARY']);
        $relation = new DoctorSecretary($doctor, $user, $clinic);
        $relation->setOnlineShareEnabled($enabled)->setOnlineSharePercent($percent);
        $this->em->persist($relation);
        $this->em->flush();

        return $relation;
    }

    private function makeOnlinePayment(Doctor $doctor, ?Clinic $clinic = null): Payment
    {
        $start       = strtotime('+30 days') + random_int(0, 500_000) * 7;
        $appointment = $this->newAppointment($doctor, $this->createUser(), $start, $start + 900, $clinic);
        $this->em->persist($appointment);

        $payment = new Payment($this->createUser(), self::GROSS, 'mock', Payment::TYPE_APPOINTMENT, '');
        $payment->setAppointment($appointment);
        $this->stampTenant($payment);
        $this->em->persist($payment);
        $this->em->flush();

        return $payment;
    }

    private function commission(): CommissionService
    {
        return static::getContainer()->get(CommissionService::class);
    }

    private function earnings(): SecretaryEarningRepository
    {
        return static::getContainer()->get(SecretaryEarningRepository::class);
    }

    public function testSecretaryEarnsItsPercentWithoutAnyRepresentation(): void
    {
        $doctor   = $this->makeDoctor();
        $relation = $this->makeSecretary($doctor, 5.0);
        $payment  = $this->makeOnlinePayment($doctor);

        $this->commission()->processAppointment($payment, null, null, $doctor->getId());

        $breakdown = static::getContainer()->get(FinancialBreakdownRepository::class)->findOneBy(['payment' => $payment]);
        self::assertNotNull($breakdown, 'نوبتِ بدون نماینده هم باید تفکیک مالی بسازد');
        self::assertSame(500_000, $breakdown->getSecretaryShareRials());          // ۵٪ از ۱۰,۰۰۰,۰۰۰
        self::assertSame(self::GROSS - 500_000, $breakdown->toArray()['system_share_rials']);

        self::assertSame(500_000, $this->earnings()->sumFor($relation->getSecretary()));
        self::assertSame(
            500_000,
            static::getContainer()->get(SettlementRepository::class)->getWalletBalance($relation->getSecretary()),
        );
    }

    public function testDisabledOrZeroPercentEarnsNothing(): void
    {
        $doctor  = $this->makeDoctor();
        $off     = $this->makeSecretary($doctor, 5.0, enabled: false);
        $zero    = $this->makeSecretary($doctor, 0.0);
        $payment = $this->makeOnlinePayment($doctor);

        $this->commission()->processAppointment($payment, null, null, $doctor->getId());

        self::assertFalse(static::getContainer()->get(FinancialBreakdownRepository::class)->existsForPayment($payment));
        self::assertSame(0, $this->earnings()->sumFor($off->getSecretary()));
        self::assertSame(0, $this->earnings()->sumFor($zero->getSecretary()));
    }

    public function testEachSecretaryGetsItsOwnPercent(): void
    {
        $doctor  = $this->makeDoctor();
        $first   = $this->makeSecretary($doctor, 5.0);
        $second  = $this->makeSecretary($doctor, 10.0);
        $payment = $this->makeOnlinePayment($doctor);

        $this->commission()->processAppointment($payment, null, null, $doctor->getId());

        self::assertSame(500_000, $this->earnings()->sumFor($first->getSecretary()));
        self::assertSame(1_000_000, $this->earnings()->sumFor($second->getSecretary()));
    }

    public function testPaymentIsNeverSplitTwice(): void
    {
        $doctor   = $this->makeDoctor();
        $relation = $this->makeSecretary($doctor, 5.0);
        $payment  = $this->makeOnlinePayment($doctor);

        $this->commission()->processAppointment($payment, null, null, $doctor->getId());
        $this->commission()->processAppointment($payment, null, null, $doctor->getId());

        self::assertSame(500_000, $this->earnings()->sumFor($relation->getSecretary()));
        self::assertSame(1, $this->earnings()->countFor($relation->getSecretary()));
    }

    /** مجموع درصدها > ۱۰۰ → کلیپ می‌شود و سهم سیستم منفی نمی‌شود. */
    public function testSharesAreClippedSoTheSystemShareStaysPositive(): void
    {
        $doctor  = $this->makeDoctor();
        $a       = $this->makeSecretary($doctor, 80.0);
        $b       = $this->makeSecretary($doctor, 60.0);
        $payment = $this->makeOnlinePayment($doctor);

        $this->commission()->processAppointment($payment, null, null, $doctor->getId());

        $breakdown = static::getContainer()->get(FinancialBreakdownRepository::class)->findOneBy(['payment' => $payment]);
        self::assertGreaterThanOrEqual(0, $breakdown->toArray()['system_share_rials']);
        self::assertSame(self::GROSS, $breakdown->getSecretaryShareRials() + $breakdown->toArray()['system_share_rials']);
        self::assertGreaterThan(0, $this->earnings()->sumFor($a->getSecretary()));
        self::assertGreaterThan(0, $this->earnings()->sumFor($b->getSecretary()));
    }

    public function testClinicAppointmentCreditsTheClinicSecretaries(): void
    {
        $doctor = $this->makeDoctor();
        $owner  = $this->createUser(['ROLE_USER', 'ROLE_CLINIC']);
        $clinic = new Clinic($owner);
        $clinic->setName('کلینیک تست سهم منشی');
        $clinic->getDoctors()->add($doctor);
        $this->em->persist($clinic);
        $this->em->flush();

        $clinicSecretary = $this->makeSecretary($doctor, 7.0, clinic: $clinic);
        $officeSecretary = $this->makeSecretary($doctor, 5.0);   // مطب شخصی — نباید سهم بگیرد

        $payment = $this->makeOnlinePayment($doctor, $clinic);
        $this->commission()->processAppointment($payment, null, null, $doctor->getId());

        self::assertSame(700_000, $this->earnings()->sumFor($clinicSecretary->getSecretary()));
        self::assertSame(0, $this->earnings()->sumFor($officeSecretary->getSecretary()));
    }

    /** سهم از «خالص» گرفته می‌شود: با مالیات و هزینهٔ پیامک، مبنا کمتر است. */
    public function testShareIsCalculatedOnTheNetAmount(): void
    {
        $config = static::getContainer()->get(SiteConfigRepository::class);
        $config->set('sms_panel_fee_rials', '1000000');
        $config->set('tax_enabled', '1');
        $config->set('tax_percent', '10');

        $doctor   = $this->makeDoctor();
        $relation = $this->makeSecretary($doctor, 10.0);
        $payment  = $this->makeOnlinePayment($doctor);

        $this->commission()->processAppointment($payment, null, null, $doctor->getId());

        // ۱۰,۰۰۰,۰۰۰ − ۱,۰۰۰,۰۰۰ = ۹,۰۰۰,۰۰۰ ؛ مالیات = 9,000,000×10/110 = 818,182
        // خالص = 8,181,818 ؛ سهم ۱۰٪ = 818,182
        self::assertSame(818_182, $this->earnings()->sumFor($relation->getSecretary()));
    }
}
