<?php
declare(strict_types=1);

namespace Tests\Unit;

use App\Modules\Accounting\ReceiptRepository;
use App\Modules\Accounting\ReceiptService;
use App\Modules\Automation\AutomationEmailOnceRepository;
use App\Modules\Automation\AutomationExecutionLogRepository;
use App\Modules\Automation\AutomationRepository;
use App\Modules\Automation\AutomationService;
use App\Modules\Email\EmailSendingService;
use App\Modules\Orders\OrdersRepository;
use App\Modules\Settings\CompanySettingsRepository;
use App\Modules\Settings\ReceiptConfigRepository;
use App\Modules\Shipments\ShipmentPackageRepository;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

final class AutomationServiceTest extends TestCase
{
    private AutomationRepository&MockObject $automationRepository;
    private AutomationExecutionLogRepository&MockObject $executionLogRepository;
    private AutomationEmailOnceRepository&MockObject $emailOnceRepository;
    private EmailSendingService&MockObject $emailService;
    private OrdersRepository&MockObject $ordersRepository;
    private CompanySettingsRepository&MockObject $companySettingsRepository;
    private ReceiptRepository&MockObject $receiptRepository;
    private ReceiptConfigRepository&MockObject $receiptConfigRepository;
    private ShipmentPackageRepository&MockObject $shipmentPackageRepository;
    private ReceiptService&MockObject $receiptService;
    private AutomationService $service;

    protected function setUp(): void
    {
        $this->automationRepository = $this->createMock(AutomationRepository::class);
        $this->executionLogRepository = $this->createMock(AutomationExecutionLogRepository::class);
        $this->emailOnceRepository = $this->createMock(AutomationEmailOnceRepository::class);
        $this->emailService = $this->createMock(EmailSendingService::class);
        $this->ordersRepository = $this->createMock(OrdersRepository::class);
        $this->companySettingsRepository = $this->createMock(CompanySettingsRepository::class);
        $this->receiptRepository = $this->createMock(ReceiptRepository::class);
        $this->receiptConfigRepository = $this->createMock(ReceiptConfigRepository::class);
        $this->shipmentPackageRepository = $this->createMock(ShipmentPackageRepository::class);
        $this->receiptService = $this->createMock(ReceiptService::class);

        $this->service = new AutomationService(
            $this->automationRepository,
            $this->executionLogRepository,
            $this->emailOnceRepository,
            $this->emailService,
            $this->ordersRepository,
            $this->companySettingsRepository,
            $this->receiptRepository,
            $this->receiptConfigRepository,
            $this->shipmentPackageRepository,
            $this->receiptService
        );
    }

    public function testSendEmailActionWithSendOncePerOrderSendsOnlyFirstTime(): void
    {
        $rule = [
            'id' => 7,
            'name' => 'Regula 1',
            'conditions' => [],
            'actions' => [[
                'id' => 11,
                'action_type' => 'send_email',
                'action_config' => [
                    'template_id' => 3,
                    'recipient' => 'client',
                    'send_once_per_order' => 1,
                ],
            ]],
        ];

        $this->automationRepository
            ->expects($this->exactly(2))
            ->method('findActiveByEvent')
            ->with('order.status_aged')
            ->willReturn([$rule]);

        $this->ordersRepository
            ->expects($this->exactly(2))
            ->method('findDetails')
            ->with(123)
            ->willReturn(['order' => ['id' => 123, 'integration_id' => 1]]);

        $this->emailOnceRepository
            ->expects($this->exactly(2))
            ->method('wasSent')
            ->with(7, 11, 123)
            ->willReturnOnConsecutiveCalls(false, true);

        $this->emailService
            ->expects($this->once())
            ->method('send')
            ->with(123, 3, null, 'Automatyzacja: Regula 1')
            ->willReturn(['success' => true, 'error' => null, 'log_id' => 1]);

        $this->emailOnceRepository
            ->expects($this->once())
            ->method('markSent')
            ->with(7, 11, 123);

        $this->executionLogRepository
            ->expects($this->exactly(2))
            ->method('create');

        $this->service->trigger('order.status_aged', 123, ['days_in_status' => 6]);
        $this->service->trigger('order.status_aged', 123, ['days_in_status' => 7]);
    }

    public function testSendEmailActionWithoutSendOncePerOrderKeepsSending(): void
    {
        $rule = [
            'id' => 8,
            'name' => 'Regula 2',
            'conditions' => [],
            'actions' => [[
                'id' => 12,
                'action_type' => 'send_email',
                'action_config' => [
                    'template_id' => 4,
                    'recipient' => 'client',
                    'send_once_per_order' => 0,
                ],
            ]],
        ];

        $this->automationRepository
            ->expects($this->exactly(2))
            ->method('findActiveByEvent')
            ->with('order.status_aged')
            ->willReturn([$rule]);

        $this->ordersRepository
            ->expects($this->exactly(2))
            ->method('findDetails')
            ->with(123)
            ->willReturn(['order' => ['id' => 123, 'integration_id' => 1]]);

        $this->emailOnceRepository
            ->expects($this->never())
            ->method('wasSent');

        $this->emailOnceRepository
            ->expects($this->never())
            ->method('markSent');

        $this->emailService
            ->expects($this->exactly(2))
            ->method('send')
            ->with(123, 4, null, 'Automatyzacja: Regula 2')
            ->willReturn(['success' => true, 'error' => null, 'log_id' => 2]);

        $this->executionLogRepository
            ->expects($this->exactly(2))
            ->method('create');

        $this->service->trigger('order.status_aged', 123, ['days_in_status' => 6]);
        $this->service->trigger('order.status_aged', 123, ['days_in_status' => 7]);
    }

    public function testOrderStatusConditionForOrderStatusAgedUsesCurrentStatus(): void
    {
        $rule = [
            'id' => 9,
            'name' => 'Regula 3',
            'conditions' => [[
                'id' => 21,
                'condition_type' => 'order_status',
                'condition_value' => [
                    'order_status_codes' => ['w_realizacji'],
                ],
            ]],
            'actions' => [[
                'id' => 13,
                'action_type' => 'send_email',
                'action_config' => [
                    'template_id' => 5,
                    'recipient' => 'client',
                    'send_once_per_order' => 0,
                ],
            ]],
        ];

        $this->automationRepository
            ->expects($this->once())
            ->method('findActiveByEvent')
            ->with('order.status_aged')
            ->willReturn([$rule]);

        $this->ordersRepository
            ->expects($this->once())
            ->method('findDetails')
            ->with(123)
            ->willReturn(['order' => ['id' => 123, 'integration_id' => 1]]);

        $this->emailService
            ->expects($this->once())
            ->method('send')
            ->with(123, 5, null, 'Automatyzacja: Regula 3')
            ->willReturn(['success' => true, 'error' => null, 'log_id' => 3]);

        $this->executionLogRepository
            ->expects($this->once())
            ->method('create');

        $this->service->trigger('order.status_aged', 123, [
            'days_in_status' => 7,
            'current_status' => 'w_realizacji',
        ]);
    }
}
