<?php
namespace Tests\Unit\Domain\Integrations;

use PHPUnit\Framework\TestCase;
use Domain\Integrations\ApiloRepository;

class ApiloRepositoryTest extends TestCase
{
    private $mockDb;
    private ApiloRepository $repository;

    protected function setUp(): void
    {
        $this->mockDb = $this->createMock(\medoo::class);
        $this->repository = new ApiloRepository($this->mockDb);
    }

    public function testApiloGetAccessTokenReturnsNullWithoutSettings(): void
    {
        $this->mockDb->method('select')->willReturn([]);

        $this->assertNull($this->repository->apiloGetAccessToken());
    }

    public function testShouldRefreshAccessTokenReturnsFalseForFarFutureDate(): void
    {
        $reflection = new \ReflectionClass($this->repository);
        $method = $reflection->getMethod('shouldRefreshAccessToken');
        $method->setAccessible(true);

        $future = date('Y-m-d H:i:s', time() + 3600);
        $result = $method->invoke($this->repository, $future, 300);

        $this->assertFalse($result);
    }

    public function testShouldRefreshAccessTokenReturnsTrueForNearExpiryDate(): void
    {
        $reflection = new \ReflectionClass($this->repository);
        $method = $reflection->getMethod('shouldRefreshAccessToken');
        $method->setAccessible(true);

        $near = date('Y-m-d H:i:s', time() + 120);
        $result = $method->invoke($this->repository, $near, 300);

        $this->assertTrue($result);
    }

    public function testApiloFetchListThrowsForInvalidType(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->repository->apiloFetchList('invalid');
    }

    public function testApiloFetchListResultReturnsDetailedErrorWhenConfigMissing(): void
    {
        $this->mockDb->expects($this->once())
            ->method('select')
            ->with('pp_shop_apilo_settings', ['name', 'value'])
            ->willReturn([]);

        $result = $this->repository->apiloFetchListResult('payment');

        $this->assertIsArray($result);
        $this->assertFalse((bool)($result['success'] ?? true));
        $this->assertStringContainsString('Brakuje konfiguracji Apilo', (string)($result['message'] ?? ''));
    }

    public function testApiloIntegrationStatusReturnsMissingConfigMessage(): void
    {
        $this->mockDb->expects($this->once())
            ->method('select')
            ->with('pp_shop_apilo_settings', ['name', 'value'])
            ->willReturn([]);

        $status = $this->repository->apiloIntegrationStatus();

        $this->assertIsArray($status);
        $this->assertFalse((bool)($status['is_valid'] ?? true));
        $this->assertStringContainsString('Brakuje konfiguracji Apilo', (string)($status['message'] ?? ''));
    }

    public function testNormalizeApiloMapListRejectsErrorPayload(): void
    {
        $reflection = new \ReflectionClass($this->repository);
        $method = $reflection->getMethod('normalizeApiloMapList');
        $method->setAccessible(true);

        $result = $method->invoke($this->repository, [
            'message' => 'Missing JWT token',
            'code' => 401,
        ]);

        $this->assertNull($result);
    }

    public function testNormalizeApiloMapListAcceptsIdNameList(): void
    {
        $reflection = new \ReflectionClass($this->repository);
        $method = $reflection->getMethod('normalizeApiloMapList');
        $method->setAccessible(true);

        $payload = [
            ['id' => '1', 'name' => 'Przelew'],
            ['id' => '2', 'name' => 'Karta'],
        ];

        $result = $method->invoke($this->repository, $payload);

        $this->assertIsArray($result);
        $this->assertCount(2, $result);
        $this->assertSame('1', (string)$result[0]['id']);
        $this->assertSame('Przelew', (string)$result[0]['name']);
    }

    public function testAllPublicMethodsExist(): void
    {
        $expectedMethods = [
            'apiloAuthorize', 'apiloGetAccessToken', 'apiloKeepalive', 'apiloIntegrationStatus',
            'apiloFetchList', 'apiloFetchListResult', 'apiloProductSearch', 'apiloCreateProduct',
        ];

        foreach ($expectedMethods as $method) {
            $this->assertTrue(
                method_exists($this->repository, $method),
                "Method $method does not exist"
            );
        }
    }
}
