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

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

class IntegrationsRepositoryTest extends TestCase
{
    private $mockDb;
    private IntegrationsRepository $repository;

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

    public function testGetSettingsReturnsArray(): void
    {
        $this->mockDb->expects($this->once())
            ->method('select')
            ->with('pp_shop_apilo_settings', ['name', 'value'])
            ->willReturn([
                ['name' => 'client-id', 'value' => 'abc123'],
                ['name' => 'client-secret', 'value' => 'secret'],
            ]);

        $settings = $this->repository->getSettings('apilo');

        $this->assertIsArray($settings);
        $this->assertSame('abc123', $settings['client-id']);
        $this->assertSame('secret', $settings['client-secret']);
    }

    public function testGetSettingReturnsValue(): void
    {
        $this->mockDb->expects($this->once())
            ->method('get')
            ->with('pp_shop_apilo_settings', 'value', ['name' => 'client-id'])
            ->willReturn('abc123');

        $this->assertSame('abc123', $this->repository->getSetting('apilo', 'client-id'));
    }

    public function testGetSettingReturnsNullWhenNotFound(): void
    {
        $this->mockDb->expects($this->once())
            ->method('get')
            ->with('pp_shop_apilo_settings', 'value', ['name' => 'nonexistent'])
            ->willReturn(false);

        $this->assertNull($this->repository->getSetting('apilo', 'nonexistent'));
    }

    public function testSaveSettingUpdatesExistingValue(): void
    {
        $this->mockDb->expects($this->once())
            ->method('count')
            ->with('pp_shop_apilo_settings', ['name' => 'client-id'])
            ->willReturn(1);

        $this->mockDb->expects($this->once())
            ->method('update')
            ->with('pp_shop_apilo_settings', ['value' => 'new-value'], ['name' => 'client-id']);

        $this->assertTrue($this->repository->saveSetting('apilo', 'client-id', 'new-value'));
    }

    public function testSaveSettingInsertsNewValue(): void
    {
        $this->mockDb->expects($this->once())
            ->method('count')
            ->with('pp_shop_shoppro_settings', ['name' => 'domain'])
            ->willReturn(0);

        $this->mockDb->expects($this->once())
            ->method('insert')
            ->with('pp_shop_shoppro_settings', ['name' => 'domain', 'value' => 'example.com']);

        $this->assertTrue($this->repository->saveSetting('shoppro', 'domain', 'example.com'));
    }

    public function testInvalidProviderThrowsException(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->repository->getSettings('sellasist');
    }

    public function testLinkProductUpdatesDatabase(): void
    {
        $this->mockDb->expects($this->once())
            ->method('update')
            ->with(
                'pp_shop_products',
                $this->callback(function ($data) {
                    return isset($data['apilo_product_id']) && isset($data['apilo_product_name']);
                }),
                ['id' => 42]
            )
            ->willReturn(1);

        $this->assertTrue($this->repository->linkProduct(42, 'ext-123', 'Test Product'));
    }

    public function testUnlinkProductClearsFields(): void
    {
        $this->mockDb->expects($this->once())
            ->method('update')
            ->with(
                'pp_shop_products',
                ['apilo_product_id' => null, 'apilo_product_name' => null],
                ['id' => 42]
            )
            ->willReturn(1);

        $this->assertTrue($this->repository->unlinkProduct(42));
    }

    public function testAllPublicMethodsExist(): void
    {
        $expectedMethods = [
            'getSettings', 'getSetting', 'saveSetting',
            'linkProduct', 'unlinkProduct',
            'getLogs', 'deleteLog', 'clearLogs',
            'getProductSku', 'shopproImportProduct',
        ];

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

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

        $this->assertIsArray($this->repository->getSettings('apilo'));
    }

    public function testShopproProviderWorks(): void
    {
        $this->mockDb->method('select')
            ->with('pp_shop_shoppro_settings', ['name', 'value'])
            ->willReturn([
                ['name' => 'domain', 'value' => 'test.com'],
            ]);

        $settings = $this->repository->getSettings('shoppro');
        $this->assertSame('test.com', $settings['domain']);
    }

    public function testGetProductSkuReturnsValue(): void
    {
        $this->mockDb->expects($this->once())
            ->method('get')
            ->with('pp_shop_products', 'sku', ['id' => 10])
            ->willReturn('SKU-100');

        $this->assertSame('SKU-100', $this->repository->getProductSku(10));
    }

    public function testGetProductSkuReturnsNullForMissing(): void
    {
        $this->mockDb->expects($this->once())
            ->method('get')
            ->with('pp_shop_products', 'sku', ['id' => 999])
            ->willReturn(false);

        $this->assertNull($this->repository->getProductSku(999));
    }

        // ── Logs ────────────────────────────────────────────────────

    public function testGetLogsReturnsItemsAndTotal(): void
    {
        $this->mockDb->expects($this->once())
            ->method('count')
            ->with('pp_log', $this->anything())
            ->willReturn(2);

        $this->mockDb->expects($this->once())
            ->method('select')
            ->with('pp_log', '*', $this->anything())
            ->willReturn([
                ['id' => 1, 'action' => 'send_order', 'message' => 'OK', 'date' => '2026-01-01 12:00:00'],
                ['id' => 2, 'action' => 'status_sync', 'message' => 'Synced', 'date' => '2026-01-02 12:00:00'],
            ]);

        $result = $this->repository->getLogs([], 'id', 'DESC', 1, 15);

        $this->assertIsArray($result);
        $this->assertArrayHasKey('items', $result);
        $this->assertArrayHasKey('total', $result);
        $this->assertCount(2, $result['items']);
        $this->assertSame(2, $result['total']);
    }

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

        $result = $this->repository->getLogs([], 'id', 'DESC', 1, 15);

        $this->assertSame(0, $result['total']);
        $this->assertEmpty($result['items']);
    }

    public function testGetLogsHandlesNullFromSelect(): void
    {
        $this->mockDb->method('count')->willReturn(0);
        $this->mockDb->method('select')->willReturn(null);

        $result = $this->repository->getLogs([], 'id', 'DESC', 1, 15);

        $this->assertSame([], $result['items']);
    }

    public function testDeleteLogCallsDelete(): void
    {
        $this->mockDb->expects($this->once())
            ->method('delete')
            ->with('pp_log', ['id' => 42]);

        $this->assertTrue($this->repository->deleteLog(42));
    }

    public function testClearLogsDeletesAll(): void
    {
        $this->mockDb->expects($this->once())
            ->method('delete')
            ->with('pp_log', []);

        $this->assertTrue($this->repository->clearLogs());
    }
}
