<?php
declare(strict_types=1);

namespace Tests\Unit\Events;

use Tests\TestCase;
use App\Core\Events\PaymentCompleted;
use App\Core\Events\PaymentVoided;
use App\Core\Events\SaleCompleted;
use App\Core\Events\SaleVoided;
use App\Core\Events\FineImposed;
use App\Core\Events\FinePaid;
use App\Core\Events\SubscriptionPaid;
use App\Core\Events\InstallmentPaid;
use App\Core\Events\InstallmentPlanCreated;
use App\Core\Events\PayrollPaid;
use App\Core\Events\MemberCreated;
use App\Core\Events\MemberActivated;
use App\Core\EventBus;

final class EventSerializationTest extends TestCase
{
    public function testPaymentCompletedRoundTrip(): void
    {
        $event = new PaymentCompleted(
            paymentId: 42,
            receiptId: 100,
            receiptNumber: 'REC-2026-001',
            memberId: 7,
            paymentType: 'annual_subscription',
            amount: '527.00',
            method: 'cash',
        );

        $this->assertEquals('payment.completed', $event::eventName());

        $array = $event->toArray();
        $this->assertEquals(42, $array['payment_id']);
        $this->assertEquals(100, $array['receipt_id']);
        $this->assertEquals('REC-2026-001', $array['receipt_number']);
        $this->assertEquals(7, $array['member_id']);
        $this->assertEquals('annual_subscription', $array['payment_type']);
        $this->assertEquals('annual_subscription', $array['type']);
        $this->assertEquals('527.00', $array['amount']);
        $this->assertEquals('cash', $array['method']);

        $restored = PaymentCompleted::from($array);
        $this->assertEquals($event->paymentId, $restored->paymentId);
        $this->assertEquals($event->amount, $restored->amount);
        $this->assertEquals($event->method, $restored->method);
    }

    public function testPaymentVoidedRoundTrip(): void
    {
        $event = new PaymentVoided(
            paymentId: 10,
            memberId: 5,
            paymentType: 'fine',
            amount: '1000.00',
            reason: 'خطأ في التسجيل',
        );

        $this->assertEquals('payment.voided', $event::eventName());
        $restored = PaymentVoided::from($event->toArray());
        $this->assertEquals(10, $restored->paymentId);
        $this->assertEquals('خطأ في التسجيل', $restored->reason);
    }

    public function testSaleCompletedRoundTrip(): void
    {
        $event = new SaleCompleted(
            saleId: 33,
            invoiceNumber: 'INV-2026-050',
            customerType: 'member',
            totalAmount: '250.00',
        );

        $this->assertEquals('sale.completed', $event::eventName());
        $restored = SaleCompleted::from($event->toArray());
        $this->assertEquals(33, $restored->saleId);
        $this->assertEquals('INV-2026-050', $restored->invoiceNumber);
    }

    public function testSaleVoidedRoundTrip(): void
    {
        $event = new SaleVoided(saleId: 33, reason: 'إرجاع');
        $this->assertEquals('sale.voided', $event::eventName());
        $restored = SaleVoided::from($event->toArray());
        $this->assertEquals(33, $restored->saleId);
        $this->assertEquals('إرجاع', $restored->reason);
    }

    public function testFineImposedRoundTrip(): void
    {
        $event = new FineImposed(
            fineId: 8,
            memberId: 12,
            penaltyType: 'financial',
            amount: '5000.00',
        );

        $this->assertEquals('fine.imposed', $event::eventName());
        $restored = FineImposed::from($event->toArray());
        $this->assertEquals(8, $restored->fineId);
        $this->assertEquals('financial', $restored->penaltyType);
    }

    public function testFinePaidRoundTrip(): void
    {
        $event = new FinePaid(fineId: 8, memberId: 12, amount: '5000.00');
        $this->assertEquals('fine.paid', $event::eventName());
        $restored = FinePaid::from($event->toArray());
        $this->assertEquals(8, $restored->fineId);
    }

    public function testSubscriptionPaidRoundTrip(): void
    {
        $event = new SubscriptionPaid(
            subscriptionId: 200,
            memberId: 15,
            year: '2026',
            amount: '527.00',
        );

        $this->assertEquals('subscription.paid', $event::eventName());
        $restored = SubscriptionPaid::from($event->toArray());
        $this->assertEquals(200, $restored->subscriptionId);
        $this->assertEquals('2026', $restored->year);
    }

    public function testInstallmentPaidRoundTrip(): void
    {
        $event = new InstallmentPaid(planId: 5, scheduleId: 12, memberId: 3);
        $this->assertEquals('installment.paid', $event::eventName());
        $restored = InstallmentPaid::from($event->toArray());
        $this->assertEquals(5, $restored->planId);
        $this->assertEquals(12, $restored->scheduleId);
    }

    public function testInstallmentPlanCreatedRoundTrip(): void
    {
        $event = new InstallmentPlanCreated(planId: 5, memberId: 3, totalAmount: '150000.00');
        $this->assertEquals('installment.plan_created', $event::eventName());
        $restored = InstallmentPlanCreated::from($event->toArray());
        $this->assertEquals(5, $restored->planId);
        $this->assertEquals('150000.00', $restored->totalAmount);
    }

    public function testPayrollPaidRoundTrip(): void
    {
        $event = new PayrollPaid(payrollRunId: 1, periodId: 6, paymentDate: '2026-05-01');
        $this->assertEquals('hr.payroll.paid', $event::eventName());
        $restored = PayrollPaid::from($event->toArray());
        $this->assertEquals(1, $restored->payrollRunId);
        $this->assertEquals('2026-05-01', $restored->paymentDate);
    }

    public function testMemberCreatedRoundTrip(): void
    {
        $event = new MemberCreated(
            memberId: 99,
            fullNameAr: 'أحمد محمد',
            status: 'potential',
            branchId: 1,
        );

        $this->assertEquals('member.created', $event::eventName());
        $array = $event->toArray();
        $this->assertEquals(99, $array['id']);
        $this->assertEquals(99, $array['member_id']);

        $restored = MemberCreated::from($array);
        $this->assertEquals(99, $restored->memberId);
        $this->assertEquals('أحمد محمد', $restored->fullNameAr);
    }

    public function testMemberActivatedRoundTrip(): void
    {
        $event = new MemberActivated(memberId: 99, membershipNumber: 'MEM-0050');
        $this->assertEquals('member.activated', $event::eventName());
        $restored = MemberActivated::from($event->toArray());
        $this->assertEquals(99, $restored->memberId);
        $this->assertEquals('MEM-0050', $restored->membershipNumber);
    }

    public function testDispatchEventIntegration(): void
    {
        $event = new PaymentCompleted(
            paymentId: 1,
            receiptId: 1,
            receiptNumber: 'TEST-001',
            memberId: 1,
            paymentType: 'fine',
            amount: '100.00',
            method: 'cash',
        );

        EventBus::dispatchEvent($event);

        $this->assertEventDispatched('payment.completed', function (array $data) {
            $this->assertEquals(1, $data['payment_id']);
            $this->assertEquals('100.00', $data['amount']);
            $this->assertEquals('fine', $data['payment_type']);
            $this->assertEquals('fine', $data['type']);
        });
    }

    public function testFromHandlesMissingKeys(): void
    {
        $event = PaymentCompleted::from([]);
        $this->assertEquals(0, $event->paymentId);
        $this->assertEquals(0, $event->memberId);
        $this->assertEquals('0.00', $event->amount);
        $this->assertEquals('cash', $event->method);
    }
}
