<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Tests\Db;

use OCA\Mitgliederverwaltung\Db\InventoryItemCategory;
use OCA\Mitgliederverwaltung\Db\InventoryItemCategoryMapper;
use OCP\DB\QueryBuilder\IExpressionBuilder;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\DB\IResult;
use OCP\IDBConnection;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

class InventoryItemCategoryMapperTest extends TestCase {

    private IDBConnection&MockObject $db;
    private IQueryBuilder&MockObject $qb;
    private IExpressionBuilder&MockObject $expr;
    private IResult&MockObject $result;

    protected function setUp(): void {
        parent::setUp();

        $this->db = $this->createMock(IDBConnection::class);
        $this->qb = $this->createMock(IQueryBuilder::class);
        $this->expr = $this->createMock(IExpressionBuilder::class);
        $this->result = $this->createMock(IResult::class);

        $this->qb->method('expr')->willReturn($this->expr);
        $this->qb->method('select')->willReturnSelf();
        $this->qb->method('from')->willReturnSelf();
        $this->qb->method('where')->willReturnSelf();
        $this->qb->method('andWhere')->willReturnSelf();
        $this->qb->method('orderBy')->willReturnSelf();
        $this->qb->method('insert')->willReturnSelf();
        $this->qb->method('delete')->willReturnSelf();
        $this->qb->method('values')->willReturnSelf();
        $this->qb->method('createNamedParameter')->willReturn(':param');
        $this->qb->method('createFunction')->willReturnCallback(fn($call) => $call);
        $this->qb->method('getSQL')->willReturn('SELECT * FROM test');
        $this->qb->method('executeStatement')->willReturn(1);

        $this->expr->method('eq')->willReturn('col = :param');
        $this->expr->method('isNull')->willReturn('col IS NULL');

        $this->db->method('getQueryBuilder')->willReturn($this->qb);
    }

    private function configureResultRows(array $rows): void {
        $fetchCalls = array_merge($rows, [false]);
        $this->result->method('fetch')
            ->willReturnOnConsecutiveCalls(...$fetchCalls);
        $this->result->method('closeCursor')->willReturn(true);
        $this->qb->method('executeQuery')->willReturn($this->result);
    }

    private function row(int $itemId = 1, int $categoryId = 1): array {
        return [
            'item_id' => $itemId,
            'category_id' => $categoryId,
        ];
    }

    public function testAttach_detach(): void {
        $mapper = new InventoryItemCategoryMapper($this->db);
        $mapper->attach(1, 1);
        $this->assertTrue(true); // No exception = success
    }

    public function testDetach(): void {
        $mapper = new InventoryItemCategoryMapper($this->db);
        $mapper->detach(1, 1);
        $this->assertTrue(true);
    }

    public function testFindByItemIdReturnsCategories(): void {
        $this->configureResultRows([
            $this->row(1, 1),
            $this->row(1, 2),
        ]);
        $mapper = new InventoryItemCategoryMapper($this->db);
        $cats = $mapper->findByItemId(1);
        $this->assertCount(2, $cats);
        $this->assertSame(1, $cats[0]->getCategoryId());
        $this->assertSame(2, $cats[1]->getCategoryId());
    }

    public function testFindByItemIdEmpty(): void {
        $this->configureResultRows([]);
        $mapper = new InventoryItemCategoryMapper($this->db);
        $cats = $mapper->findByItemId(999);
        $this->assertCount(0, $cats);
    }
}
