<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Tests\Service;

use OCA\Mitgliederverwaltung\Db\GeneralMaterial;
use OCA\Mitgliederverwaltung\Db\GeneralMaterialMapper;
use OCA\Mitgliederverwaltung\Db\InventoryCategory;
use OCA\Mitgliederverwaltung\Db\InventoryCategoryMapper;
use OCA\Mitgliederverwaltung\Db\InventoryItemCategory;
use OCA\Mitgliederverwaltung\Db\InventoryItemCategoryMapper;
use OCA\Mitgliederverwaltung\Db\StockItem;
use OCA\Mitgliederverwaltung\Db\StockItemMapper;
use OCA\Mitgliederverwaltung\Db\StockVariant;
use OCA\Mitgliederverwaltung\Db\StockVariantMapper;
use OCA\Mitgliederverwaltung\Db\SaleRecord;
use OCA\Mitgliederverwaltung\Db\SaleRecordMapper;
use OCA\Mitgliederverwaltung\Service\InventoryReportService;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;

class InventoryReportServiceTest extends TestCase {

    private InventoryReportService $service;
    private MockObject $generalMaterialMapper;
    private MockObject $categoryMapper;
    private MockObject $itemCategoryMapper;
    private MockObject $stockItemMapper;
    private MockObject $stockVariantMapper;
    private MockObject $saleRecordMapper;
    private LoggerInterface&MockObject $logger;

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

        $this->generalMaterialMapper = $this->createMock(GeneralMaterialMapper::class);
        $this->categoryMapper = $this->createMock(InventoryCategoryMapper::class);
        $this->itemCategoryMapper = $this->createMock(InventoryItemCategoryMapper::class);
        $this->stockItemMapper = $this->createMock(StockItemMapper::class);
        $this->stockVariantMapper = $this->createMock(StockVariantMapper::class);
        $this->saleRecordMapper = $this->createMock(SaleRecordMapper::class);
        $this->logger = $this->createMock(LoggerInterface::class);

        $this->service = new InventoryReportService(
            $this->generalMaterialMapper,
            $this->categoryMapper,
            $this->itemCategoryMapper,
            $this->stockItemMapper,
            $this->stockVariantMapper,
            $this->saleRecordMapper,
            $this->logger
        );
    }

    private function generalMaterialEntity(int $id = 1, string $name = 'Zelt A', ?int $condition = 1): GeneralMaterial {
        $entity = new GeneralMaterial();
        $entity->setId($id);
        $entity->setName($name);
        $entity->setCondition($condition);
        $entity->setNotes(null);
        return $entity;
    }

    private function categoryEntity(int $id = 1, string $name = 'Zelte'): InventoryCategory {
        $entity = new InventoryCategory();
        $entity->setId($id);
        $entity->setName($name);
        return $entity;
    }

    private function itemCategoryEntity(int $itemId = 1, int $categoryId = 1): InventoryItemCategory {
        $entity = new InventoryItemCategory();
        $entity->setItemId($itemId);
        $entity->setCategoryId($categoryId);
        return $entity;
    }

    private function saleEntity(int $id = 1, int $stockItemId = 1, string $quantity = '2', string $unitPrice = '25.00'): SaleRecord {
        $entity = new SaleRecord();
        $entity->setId($id);
        $entity->setStockItemId($stockItemId);
        $entity->setVariantId(null);
        $entity->setDate('2026-04-01');
        $entity->setQuantity((int)$quantity);
        $entity->setUnitPrice($unitPrice);
        $entity->setTotalPrice((string)((float)$quantity * (float)$unitPrice));
        $entity->setNotes(null);
        $entity->setCreatedAt('2026-04-01 00:00:00');
        return $entity;
    }

    public function testGenerateSalesReport_dateFiltered(): void {
        $sales = [
            $this->saleEntity(1, 1, '2', '25.00'),
            $this->saleEntity(2, 1, '1', '30.00'),
        ];

        $this->saleRecordMapper->method('findByDateRange')
            ->with('2026-01-01', '2026-03-31')
            ->willReturn($sales);

        $report = $this->service->generateSalesReport('2026-01-01', '2026-03-31');

        $this->assertStringContainsString('2026-01-01', $report['title']);
        $this->assertCount(2, $report['rows']);
        $this->assertArrayHasKey('total_sales', $report['summary']);
        $this->assertSame(2, (int)$report['summary']['total_sales']);
    }

    public function testGenerateConditionReport_summaryByCategory(): void {
        $item1 = $this->generalMaterialEntity(1, 'Zelt A', 3);
        $item2 = $this->generalMaterialEntity(2, 'Zelt B', 4);

        $this->generalMaterialMapper->method('findAll')
            ->willReturn([$item1, $item2]);

        $itemCat1 = $this->itemCategoryEntity(1, 1);
        $this->itemCategoryMapper->method('findByItemId')
            ->willReturnCallback(function($itemId) use ($itemCat1) {
                if ($itemId === 1) {
                    return [$itemCat1];
                }
                return [];
            });

        $catEntity = $this->categoryEntity(1, 'Zelte');
        $this->categoryMapper->method('findById')
            ->willReturn($catEntity);

        // Also need findNeedingRepair to return empty
        $this->generalMaterialMapper->method('findNeedingRepair')
            ->willReturn([]);

        $report = $this->service->generateConditionReport();

        $this->assertArrayHasKey('Zelte', $report['summary']);
        $this->assertSame(1, $report['summary']['Zelte']);
    }

    public function testGenerateConditionReport_needsRepairOnly(): void {
        // One good item, one needing repair
        $goodItem = $this->generalMaterialEntity(1, 'Zelt A', 4);
        $badItem = $this->generalMaterialEntity(2, 'Zelt B', 1);

        $this->generalMaterialMapper->method('findAll')
            ->willReturn([$goodItem, $badItem]);

        $this->itemCategoryMapper->method('findByItemId')
            ->willReturn([]);

        $this->generalMaterialMapper->method('findNeedingRepair')
            ->willReturn([$badItem]);

        $report = $this->service->generateConditionReport();

        // Only the bad item should be in rows
        $this->assertCount(1, $report['rows']);
        $this->assertSame('Zelt B', $report['rows'][0]['name']);
        $this->assertSame('1/5', $report['rows'][0]['condition']);
    }
}
