<?php
declare(strict_types=1);

namespace Tests\Unit;

use App\Core\Exceptions\AllegroApiException;
use App\Modules\Orders\OrderImportRepository;
use App\Modules\Orders\OrdersRepository;
use App\Modules\Settings\AllegroApiClient;
use App\Modules\Settings\AllegroIntegrationRepository;
use App\Modules\Settings\AllegroOrderImportService;
use App\Modules\Settings\AllegroStatusMappingRepository;
use App\Modules\Settings\AllegroTokenManager;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use RuntimeException;

final class AllegroOrderImportServiceTest extends TestCase
{
    private AllegroIntegrationRepository&MockObject $integrationRepository;
    private AllegroTokenManager&MockObject $tokenManager;
    private AllegroApiClient&MockObject $apiClient;
    private OrderImportRepository&MockObject $orders;
    private AllegroStatusMappingRepository&MockObject $statusMappings;
    private OrdersRepository&MockObject $ordersRepository;
    private AllegroOrderImportService $service;

    protected function setUp(): void
    {
        $this->integrationRepository = $this->createMock(AllegroIntegrationRepository::class);
        $this->tokenManager = $this->createMock(AllegroTokenManager::class);
        $this->apiClient = $this->createMock(AllegroApiClient::class);
        $this->orders = $this->createMock(OrderImportRepository::class);
        $this->statusMappings = $this->createMock(AllegroStatusMappingRepository::class);
        $this->ordersRepository = $this->createMock(OrdersRepository::class);

        $this->service = new AllegroOrderImportService(
            $this->integrationRepository,
            $this->tokenManager,
            $this->apiClient,
            $this->orders,
            $this->statusMappings,
            $this->ordersRepository
        );
    }

    public function testImportSingleOrderHappyPath(): void
    {
        $checkoutFormId = 'abc-123';
        $payload = $this->buildMinimalPayload($checkoutFormId);

        $this->tokenManager
            ->method('resolveToken')
            ->willReturn(['access-token-xyz', 'sandbox']);

        $this->apiClient
            ->method('getCheckoutForm')
            ->with('sandbox', 'access-token-xyz', $checkoutFormId)
            ->willReturn($payload);

        $this->apiClient
            ->method('getCheckoutFormShipments')
            ->willReturn([]);

        $this->integrationRepository
            ->method('getActiveIntegrationId')
            ->willReturn(5);

        $this->statusMappings
            ->method('findMappedOrderproStatusCode')
            ->willReturn('new');

        $this->orders
            ->expects($this->once())
            ->method('upsertOrderAggregate')
            ->willReturn(['order_id' => 42, 'created' => true]);

        $this->ordersRepository
            ->expects($this->once())
            ->method('recordActivity')
            ->with(42, 'import', $this->stringContains('Zaimportowano'));

        $result = $this->service->importSingleOrder($checkoutFormId);

        $this->assertSame(42, $result['order_id']);
        $this->assertTrue($result['created']);
        $this->assertSame($checkoutFormId, $result['source_order_id']);
    }

    public function testImportSingleOrderRetryOn401(): void
    {
        $checkoutFormId = 'retry-456';
        $payload = $this->buildMinimalPayload($checkoutFormId);

        $this->tokenManager
            ->method('resolveToken')
            ->willReturnOnConsecutiveCalls(
                ['stale-token', 'sandbox'],
                ['fresh-token', 'sandbox']
            );

        $callCount = 0;
        $this->apiClient
            ->method('getCheckoutForm')
            ->willReturnCallback(function () use (&$callCount, $payload): array {
                $callCount++;
                if ($callCount === 1) {
                    throw new RuntimeException('ALLEGRO_HTTP_401');
                }
                return $payload;
            });

        $this->apiClient->method('getCheckoutFormShipments')->willReturn([]);
        $this->integrationRepository->method('getActiveIntegrationId')->willReturn(1);
        $this->statusMappings->method('findMappedOrderproStatusCode')->willReturn(null);

        $this->orders
            ->expects($this->once())
            ->method('upsertOrderAggregate')
            ->willReturn(['order_id' => 99, 'created' => true]);

        $result = $this->service->importSingleOrder($checkoutFormId);

        $this->assertSame(99, $result['order_id']);
        $this->assertSame(2, $callCount);
    }

    public function testImportSingleOrderThrowsOnEmptyId(): void
    {
        $this->expectException(AllegroApiException::class);
        $this->expectExceptionMessage('Podaj ID zamowienia');

        $this->service->importSingleOrder('');
    }

    public function testImportSingleOrderPropagatesNon401RuntimeException(): void
    {
        $this->tokenManager
            ->method('resolveToken')
            ->willReturn(['token', 'sandbox']);

        $this->apiClient
            ->method('getCheckoutForm')
            ->willThrowException(new RuntimeException('ALLEGRO_HTTP_500'));

        $this->expectException(RuntimeException::class);
        $this->expectExceptionMessage('ALLEGRO_HTTP_500');

        $this->service->importSingleOrder('order-789');
    }

    public function testImportSingleOrderReturnsCorrectFieldsOnReImport(): void
    {
        $checkoutFormId = 'reimport-001';
        $payload = $this->buildMinimalPayload($checkoutFormId);

        $this->tokenManager->method('resolveToken')->willReturn(['tok', 'production']);
        $this->apiClient->method('getCheckoutForm')->willReturn($payload);
        $this->apiClient->method('getCheckoutFormShipments')->willReturn([]);
        $this->integrationRepository->method('getActiveIntegrationId')->willReturn(1);
        $this->statusMappings->method('findMappedOrderproStatusCode')->willReturn(null);

        $this->orders
            ->method('upsertOrderAggregate')
            ->willReturn(['order_id' => 10, 'created' => false]);

        $this->ordersRepository
            ->expects($this->once())
            ->method('recordActivity')
            ->with(10, 'import', $this->stringContains('Zaktualizowano'));

        $result = $this->service->importSingleOrder($checkoutFormId);

        $this->assertSame(10, $result['order_id']);
        $this->assertFalse($result['created']);
        $this->assertArrayHasKey('image_diagnostics', $result);
    }

    /**
     * @return array<string, mixed>
     */
    private function buildMinimalPayload(string $checkoutFormId): array
    {
        return [
            'id' => $checkoutFormId,
            'status' => 'READY_FOR_PROCESSING',
            'fulfillment' => ['status' => 'NEW'],
            'payment' => [
                'id' => 'pay-1',
                'type' => 'allegro',
                'status' => 'paid',
                'amount' => ['amount' => '99.00', 'currency' => 'PLN'],
                'paidAmount' => ['amount' => '99.00', 'currency' => 'PLN'],
            ],
            'summary' => [
                'totalToPay' => ['amount' => '99.00', 'currency' => 'PLN'],
            ],
            'buyer' => [
                'login' => 'test-buyer',
                'firstName' => 'Jan',
                'lastName' => 'Kowalski',
                'email' => 'jan@example.com',
                'phoneNumber' => '500100200',
            ],
            'delivery' => [
                'method' => ['id' => 'inpost', 'name' => 'InPost Paczkomaty'],
                'address' => [
                    'firstName' => 'Jan',
                    'lastName' => 'Kowalski',
                    'street' => 'ul. Testowa 1',
                    'city' => 'Warszawa',
                    'zipCode' => '00-001',
                    'countryCode' => 'PL',
                ],
            ],
            'lineItems' => [
                [
                    'id' => 'item-1',
                    'offer' => ['id' => 'offer-1', 'name' => 'Produkt testowy'],
                    'quantity' => 2,
                    'originalPrice' => ['amount' => '49.50', 'currency' => 'PLN'],
                ],
            ],
            'invoice' => [],
            'marketplace' => ['id' => 'allegro-pl'],
            'boughtAt' => '2026-03-14T10:00:00Z',
            'updatedAt' => '2026-03-14T10:05:00Z',
        ];
    }
}
