<?php

namespace MintyPHP\Tests\Module\Helpdesk\Service;

use MintyPHP\Module\Helpdesk\Repository\SoftwareProductRepository;
use MintyPHP\Module\Helpdesk\Service\BcODataGateway;
use MintyPHP\Module\Helpdesk\Service\HelpdeskSettingsGateway;
use MintyPHP\Module\Helpdesk\Service\SoftwareProductSyncService;
use PHPUnit\Framework\TestCase;

class SoftwareProductSyncServiceTest extends TestCase
{
    private function createService(
        ?BcODataGateway $gateway = null,
        ?SoftwareProductRepository $repository = null,
        bool $isConfigured = true
    ): SoftwareProductSyncService {
        $gateway = $gateway ?? $this->createMock(BcODataGateway::class);
        $repository = $repository ?? $this->createMock(SoftwareProductRepository::class);
        $settingsGateway = $this->createMock(HelpdeskSettingsGateway::class);
        $settingsGateway->method('isConfigured')->willReturn($isConfigured);

        return new SoftwareProductSyncService($gateway, $repository, $settingsGateway);
    }

    public function testSyncSkipsWhenNotConfigured(): void
    {
        $service = $this->createService(isConfigured: false);
        $result = $service->sync();

        $this->assertSame('skipped', $result['status']);
        $this->assertSame('bc_not_configured', $result['error_code']);
    }

    public function testSyncReturnsFailedOnGatewayException(): void
    {
        $gateway = $this->createMock(BcODataGateway::class);
        $gateway->method('listContractTypes')
            ->willThrowException(new \RuntimeException('Connection refused'));

        $service = $this->createService($gateway);
        $result = $service->sync();

        $this->assertSame('failed', $result['status']);
        $this->assertSame('bc_fetch_failed', $result['error_code']);
        $this->assertStringContainsString('Connection refused', $result['error_message']);
    }

    public function testSyncUpsertsAndSoftDeletesCorrectly(): void
    {
        $bcTypes = [
            ['Code' => 'INFO', 'Description' => 'Infopoints'],
            ['Code' => 'WEBSITE', 'Description' => 'Saas-Gebuehren Website'],
        ];

        $gateway = $this->createMock(BcODataGateway::class);
        $gateway->method('listContractTypes')->willReturn($bcTypes);

        $repository = $this->createMock(SoftwareProductRepository::class);
        $repository->expects($this->exactly(2))
            ->method('upsertByCode')
            ->willReturnCallback(function (string $code, string $desc): bool {
                $this->assertContains($code, ['INFO', 'WEBSITE']);

                return true;
            });

        $repository->expects($this->once())
            ->method('softDeleteNotInCodes')
            ->with(['INFO', 'WEBSITE'])
            ->willReturn(1);

        $service = $this->createService($gateway, $repository);
        $result = $service->sync();

        $this->assertSame('success', $result['status']);
        $this->assertNull($result['error_code']);
        $this->assertSame(2, $result['result']['bc_count']);
        $this->assertSame(2, $result['result']['upserted']);
        $this->assertSame(1, $result['result']['deactivated']);
    }

    public function testSyncSkipsWhenBcReturnsEmpty(): void
    {
        $gateway = $this->createMock(BcODataGateway::class);
        $gateway->method('listContractTypes')->willReturn([]);

        $repository = $this->createMock(SoftwareProductRepository::class);
        $repository->expects($this->never())->method('upsertByCode');
        $repository->expects($this->never())->method('softDeleteNotInCodes');

        $service = $this->createService($gateway, $repository);
        $result = $service->sync();

        $this->assertSame('skipped', $result['status']);
        $this->assertSame('bc_empty_response', $result['error_code']);
    }

    public function testSyncIsIdempotent(): void
    {
        $bcTypes = [
            ['Code' => 'INFO', 'Description' => 'Infopoints'],
        ];

        $gateway = $this->createMock(BcODataGateway::class);
        $gateway->method('listContractTypes')->willReturn($bcTypes);

        $repository = $this->createMock(SoftwareProductRepository::class);
        $repository->method('upsertByCode')->willReturn(true);
        $repository->method('softDeleteNotInCodes')->willReturn(0);

        $service = $this->createService($gateway, $repository);

        $result1 = $service->sync();
        $result2 = $service->sync();

        $this->assertSame('success', $result1['status']);
        $this->assertSame('success', $result2['status']);
        $this->assertSame($result1['result']['bc_count'], $result2['result']['bc_count']);
    }
}
