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

use PHPUnit\Framework\TestCase;
use Domain\ShopStatus\ShopStatusRepository;

class ShopStatusRepositoryTest extends TestCase
{
    public function testFindReturnsNullForNegativeId(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $mockDb->expects($this->never())->method('get');

        $repository = new ShopStatusRepository($mockDb);
        $this->assertNull($repository->find(-1));
    }

    public function testFindReturnsNullWhenNotFound(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $mockDb->expects($this->once())
            ->method('get')
            ->with('pp_shop_statuses', '*', ['id' => 99])
            ->willReturn(null);

        $repository = new ShopStatusRepository($mockDb);
        $this->assertNull($repository->find(99));
    }

    public function testFindReturnsStatusWithIdZero(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $mockDb->expects($this->once())
            ->method('get')
            ->with('pp_shop_statuses', '*', ['id' => 0])
            ->willReturn([
                'id' => '0',
                'status' => 'zamówienie złożone',
                'color' => '#ff0000',
                'o' => '0',
                'apilo_status_id' => '5',
            ]);

        $repository = new ShopStatusRepository($mockDb);
        $result = $repository->find(0);

        $this->assertIsArray($result);
        $this->assertSame(0, $result['id']);
        $this->assertSame('zamówienie złożone', $result['status']);
        $this->assertSame('#ff0000', $result['color']);
        $this->assertSame(0, $result['o']);
        $this->assertSame(5, $result['apilo_status_id']);
    }

    public function testFindNormalizesNullApiloStatusId(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $mockDb->expects($this->once())
            ->method('get')
            ->with('pp_shop_statuses', '*', ['id' => 1])
            ->willReturn([
                'id' => '1',
                'status' => 'zamówienie opłacone',
                'color' => '',
                'o' => '1',
                'apilo_status_id' => null,
            ]);

        $repository = new ShopStatusRepository($mockDb);
        $result = $repository->find(1);

        $this->assertIsArray($result);
        $this->assertSame(1, $result['id']);
        $this->assertNull($result['apilo_status_id']);
    }

    public function testSaveUpdatesColorAndApiloStatusId(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $updateRow = null;
        $updateWhere = null;

        $mockDb->expects($this->once())
            ->method('update')
            ->willReturnCallback(function ($table, $row, $where) use (&$updateRow, &$updateWhere) {
                $this->assertSame('pp_shop_statuses', $table);
                $updateRow = $row;
                $updateWhere = $where;
                return true;
            });

        $repository = new ShopStatusRepository($mockDb);
        $id = $repository->save(3, [
            'color' => ' #00ff00 ',
            'apilo_status_id' => '12',
        ]);

        $this->assertSame(3, $id);
        $this->assertSame('#00ff00', $updateRow['color']);
        $this->assertSame(12, $updateRow['apilo_status_id']);
        $this->assertSame(['id' => 3], $updateWhere);
    }

    public function testSaveWithIdZeroWorks(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $mockDb->expects($this->once())
            ->method('update')
            ->with('pp_shop_statuses', $this->anything(), ['id' => 0]);

        $repository = new ShopStatusRepository($mockDb);
        $id = $repository->save(0, ['color' => '#aabbcc']);

        $this->assertSame(0, $id);
    }

    public function testSaveWithEmptyApiloStatusIdSetsNull(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $updateRow = null;

        $mockDb->expects($this->once())
            ->method('update')
            ->willReturnCallback(function ($table, $row) use (&$updateRow) {
                $updateRow = $row;
            });

        $repository = new ShopStatusRepository($mockDb);
        $repository->save(1, ['color' => '#000', 'apilo_status_id' => '']);

        $this->assertNull($updateRow['apilo_status_id']);
    }

    public function testSaveRejectsNegativeId(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $mockDb->expects($this->never())->method('update');

        $repository = new ShopStatusRepository($mockDb);
        $this->assertSame(0, $repository->save(-1, ['color' => '#fff']));
    }

    public function testGetApiloStatusIdReturnsValue(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $mockDb->expects($this->once())
            ->method('get')
            ->with('pp_shop_statuses', 'apilo_status_id', ['id' => 4])
            ->willReturn(15);

        $repository = new ShopStatusRepository($mockDb);
        $this->assertSame(15, $repository->getApiloStatusId(4));
    }

    public function testGetApiloStatusIdReturnsNullWhenNotSet(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $mockDb->expects($this->once())
            ->method('get')
            ->willReturn(null);

        $repository = new ShopStatusRepository($mockDb);
        $this->assertNull($repository->getApiloStatusId(4));
    }

    public function testGetByIntegrationStatusIdForApilo(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $mockDb->expects($this->once())
            ->method('get')
            ->with('pp_shop_statuses', 'id', ['apilo_status_id' => 15])
            ->willReturn(4);

        $repository = new ShopStatusRepository($mockDb);
        $this->assertSame(4, $repository->getByIntegrationStatusId('apilo', 15));
    }

    public function testGetByIntegrationStatusIdReturnsNullForUnknownIntegration(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $mockDb->expects($this->never())->method('get');

        $repository = new ShopStatusRepository($mockDb);
        $this->assertNull($repository->getByIntegrationStatusId('unknown', 1));
    }

    public function testAllStatusesReturnsOrderedList(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $mockDb->expects($this->once())
            ->method('select')
            ->with('pp_shop_statuses', ['id', 'status'], ['ORDER' => ['o' => 'ASC']])
            ->willReturn([
                ['id' => 0, 'status' => 'złożone'],
                ['id' => 1, 'status' => 'opłacone'],
            ]);

        $repository = new ShopStatusRepository($mockDb);
        $result = $repository->allStatuses();

        $this->assertSame([0 => 'złożone', 1 => 'opłacone'], $result);
    }

    public function testListForAdminWhitelistsSortAndDirection(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $queries = [];

        $mockDb->method('query')
            ->willReturnCallback(function ($sql, $params = []) use (&$queries) {
                $queries[] = ['sql' => $sql, 'params' => $params];

                if (strpos($sql, 'COUNT(0)') !== false) {
                    return new class {
                        public function fetchAll()
                        {
                            return [[2]];
                        }
                    };
                }

                return new class {
                    public function fetchAll()
                    {
                        return [[
                            'id' => 0,
                            'status' => 'złożone',
                            'color' => '#fff',
                            'o' => 0,
                            'apilo_status_id' => null,
                        ]];
                    }
                };
            });

        $repository = new ShopStatusRepository($mockDb);
        $result = $repository->listForAdmin(
            [],
            'status DESC; DROP TABLE pp_shop_statuses; --',
            'DESC; DELETE FROM pp_users; --',
            1,
            999
        );

        $this->assertCount(2, $queries);
        $dataSql = $queries[1]['sql'];

        $this->assertMatchesRegularExpression('/ORDER BY\s+ss\.o\s+ASC,\s+ss\.id\s+ASC/i', $dataSql);
        $this->assertStringNotContainsString('DROP TABLE', $dataSql);
        $this->assertStringNotContainsString('DELETE FROM pp_users', $dataSql);
        $this->assertMatchesRegularExpression('/LIMIT\s+100\s+OFFSET\s+0/i', $dataSql);
    }
}
