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

use PHPUnit\Framework\TestCase;
use Domain\Category\CategoryRepository;

class CategoryRepositoryTest extends TestCase
{
    public function testSortTypesReturnsExpectedKeys(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $repository = new CategoryRepository($mockDb);

        $types = $repository->sortTypes();

        $this->assertIsArray($types);
        $this->assertArrayHasKey(0, $types);
        $this->assertArrayHasKey(6, $types);
        $this->assertSame('ręczne', $types[4]);
    }

    public function testCategoryDetailsReturnsDefaultForInvalidId(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $repository = new CategoryRepository($mockDb);

        $result = $repository->categoryDetails(0);

        $this->assertIsArray($result);
        $this->assertSame(0, (int)$result['id']);
        $this->assertSame(1, (int)$result['status']);
        $this->assertNull($result['parent_id']);
        $this->assertSame([], $result['languages']);
    }

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

        $mockDb->method('get')
            ->willReturnCallback(function ($table, $columns, $where) {
                if ($table === 'pp_shop_categories') {
                    return [
                        'id' => 15,
                        'status' => '1',
                        'parent_id' => null,
                        'sort_type' => 4,
                        'view_subcategories' => '1',
                    ];
                }
                return null;
            });

        $mockDb->method('select')
            ->willReturnCallback(function ($table, $columns, $where) {
                if ($table === 'pp_shop_categories_langs') {
                    return [
                        ['lang_id' => 'pl', 'title' => 'Kategoria PL'],
                        ['lang_id' => 'en', 'title' => 'Category EN'],
                    ];
                }
                return [];
            });

        $repository = new CategoryRepository($mockDb);
        $result = $repository->categoryDetails(15);

        $this->assertSame(15, (int)$result['id']);
        $this->assertSame(1, (int)$result['status']);
        $this->assertSame(1, (int)$result['view_subcategories']);
        $this->assertSame('Kategoria PL', $result['languages']['pl']['title']);
        $this->assertSame('Category EN', $result['languages']['en']['title']);
    }

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

        $repository = new CategoryRepository($mockDb);

        $this->assertFalse($repository->saveCategoriesOrder('x'));
    }

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

        $updates = [];
        $mockDb->method('update')
            ->willReturnCallback(function ($table, $row, $where = null) use (&$updates) {
                $updates[] = [$table, $row, $where];
                return true;
            });

        $repository = new CategoryRepository($mockDb);

        $result = $repository->saveCategoriesOrder([
            ['item_id' => 10, 'parent_id' => null],
            ['item_id' => 11, 'parent_id' => 10],
            ['item_id' => 0, 'parent_id' => 11],
        ]);

        $this->assertTrue($result);
        $this->assertGreaterThanOrEqual(3, count($updates));

        $this->assertSame('pp_shop_categories', $updates[0][0]);
        $this->assertSame(['o' => 0], $updates[0][1]);

        $this->assertSame(['o' => 1, 'parent_id' => null], $updates[1][1]);
        $this->assertSame(['id' => 10], $updates[1][2]);

        $this->assertSame(['o' => 2, 'parent_id' => 10], $updates[2][1]);
        $this->assertSame(['id' => 11], $updates[2][2]);
    }

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

        $repository = new CategoryRepository($mockDb);

        $this->assertFalse($repository->saveProductOrder(0, []));
        $this->assertFalse($repository->saveProductOrder(5, 'x'));
    }

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

        $updates = [];
        $mockDb->method('update')
            ->willReturnCallback(function ($table, $row, $where = null) use (&$updates) {
                $updates[] = [$table, $row, $where];
                return true;
            });

        $repository = new CategoryRepository($mockDb);

        $result = $repository->saveProductOrder(7, [
            ['item_id' => 101],
            ['item_id' => 102],
        ]);

        $this->assertTrue($result);
        $this->assertGreaterThanOrEqual(3, count($updates));

        $this->assertSame('pp_shop_products_categories', $updates[0][0]);
        $this->assertSame(['o' => 0], $updates[0][1]);
        $this->assertSame(['category_id' => 7], $updates[0][2]);

        $this->assertSame(['o' => 1], $updates[1][1]);
        $this->assertSame(['AND' => ['category_id' => 7, 'product_id' => 101]], $updates[1][2]);
    }

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

        $mockDb->expects($this->once())
            ->method('count')
            ->with('pp_shop_categories', ['parent_id' => 5])
            ->willReturn(1);

        $mockDb->expects($this->never())->method('delete');

        $repository = new CategoryRepository($mockDb);

        $this->assertFalse($repository->categoryDelete(5));
    }

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

        $mockDb->method('count')->willReturn(0);
        $mockDb->expects($this->once())
            ->method('delete')
            ->with('pp_shop_categories', ['id' => 8])
            ->willReturn(true);

        $repository = new CategoryRepository($mockDb);

        $this->assertTrue($repository->categoryDelete(8));
    }

    public function testCategoryTitleReturnsEmptyWhenNotFound(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $mockDb->method('select')->willReturn([]);

        $repository = new CategoryRepository($mockDb);

        $this->assertSame('', $repository->categoryTitle(10));
    }

    public function testCategoryTitleReturnsFirstAvailableTitle(): void
    {
        $mockDb = $this->createMock(\medoo::class);
        $mockDb->method('select')
            ->willReturn(['Kategoria testowa']);

        $repository = new CategoryRepository($mockDb);

        $this->assertSame('Kategoria testowa', $repository->categoryTitle(10));
    }
}
