<?php

declare(strict_types=1);

namespace Ferienpass\AdminBundle\Tests\Components;

use Ferienpass\AdminBundle\Components\SettleAttendances;
use Ferienpass\CoreBundle\Entity\Attendance;
use Ferienpass\CoreBundle\Entity\Debtor;
use Ferienpass\CoreBundle\Repository\AttendanceRepository;
use Ferienpass\CoreBundle\Repository\DebtorRepository;
use PHPUnit\Framework\TestCase;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

class SettleAttendancesTest extends TestCase
{
    public function testRemoveItem(): void
    {
        $attendanceRepository = $this->createStub(AttendanceRepository::class);
        $eventDispatcher = $this->createStub(EventDispatcherInterface::class);

        $component = new SettleAttendances($attendanceRepository, $eventDispatcher);

        $component->ids = [1, 2];
        $component->items = [
            [
                'amount' => 10.0,
                'label' => 'Offer 1',
                'attendanceId' => 1,
                'type' => 'attendance',
            ],
            [
                'amount' => 20.0,
                'label' => 'Offer 2',
                'attendanceId' => 2,
                'type' => 'attendance',
            ],
        ];

        $component->removeItem(0);

        $this->assertSame([2], $component->ids);
        $this->assertCount(1, $component->items);
        $this->assertSame(2, $component->items[0]['attendanceId']);
        $this->assertEquals(20.0, $component->getTotalAmount());
    }

    public function testRemoveBalanceItem(): void
    {
        $attendanceRepository = $this->createStub(AttendanceRepository::class);
        $eventDispatcher = $this->createStub(EventDispatcherInterface::class);

        $component = new SettleAttendances($attendanceRepository, $eventDispatcher);

        $component->ids = [1];
        $component->items = [
            [
                'amount' => 10.0,
                'label' => 'Offer 1',
                'attendanceId' => 1,
                'type' => 'attendance',
            ],
            [
                'amount' => -5.0,
                'label' => 'Guthaben',
                'attendanceId' => null,
                'type' => 'discount',
            ],
        ];

        $component->removeItem(1);

        $this->assertSame([1], $component->ids);
        $this->assertCount(1, $component->items);
        $this->assertSame(1, $component->items[0]['attendanceId']);
    }

    public function testToggleUseBalance(): void
    {
        $component = new SettleAttendances(
            $this->createStub(AttendanceRepository::class),
            $this->createStub(EventDispatcherInterface::class)
        );

        self::assertTrue($component->useBalance); // default is true
        $component->toggleUseBalance();
        self::assertFalse($component->useBalance);
        $component->toggleUseBalance();
        self::assertTrue($component->useBalance);
    }

    public function testOnBalanceChangedDoesNothingWithNullDebtorId(): void
    {
        $component = new SettleAttendances(
            $this->createStub(AttendanceRepository::class),
            $this->createStub(EventDispatcherInterface::class)
        );

        // debtorId is null by default — method should return early without throwing
        $debtorRepository = $this->createStub(DebtorRepository::class);
        $component->onBalanceChanged($debtorRepository);

        // If we got here without exception, the early return worked
        $this->assertNull($component->debtorId);
    }

    public function testOnBalanceChangedReturnsEarlyWhenDebtorNotFound(): void
    {
        $component = new SettleAttendances(
            $this->createStub(AttendanceRepository::class),
            $this->createStub(EventDispatcherInterface::class)
        );
        $component->debtorId = \Symfony\Component\Uid\Uuid::v7()->toBase58();
        $component->items = [['amount' => 10.0, 'type' => 'attendance']];

        $debtorRepository = $this->createStub(DebtorRepository::class);
        $debtorRepository->method('findOneBy')->willReturn(null);

        $component->onBalanceChanged($debtorRepository);

        // Items unchanged — early return before modification
        $this->assertCount(1, $component->items);
    }

    public function testOnBalanceChangedUpdatesExistingDiscountItemWhenBalancePositive(): void
    {
        $component = new SettleAttendances(
            $this->createStub(AttendanceRepository::class),
            $this->createStub(EventDispatcherInterface::class)
        );
        $component->debtorId = \Symfony\Component\Uid\Uuid::v7()->toBase58();
        $component->items = [
            ['amount' => 10.0, 'type' => 'attendance', 'attendanceId' => 1],
            ['amount' => -2.0, 'type' => 'discount', 'attendanceId' => null],
        ];

        $debtor = $this->createMock(Debtor::class);
        $debtor->method('getBalance')->willReturn(500); // 5.00 EUR in cents

        $debtorRepository = $this->createStub(DebtorRepository::class);
        $debtorRepository->method('findOneBy')->willReturn($debtor);

        $component->onBalanceChanged($debtorRepository);

        $discountItem = array_values(array_filter($component->items, fn ($i) => 'discount' === $i['type']))[0];
        $this->assertEqualsWithDelta(-5.0, $discountItem['amount'], 0.001);
    }

    public function testOnBalanceChangedRemovesDiscountItemWhenBalanceNotPositive(): void
    {
        $component = new SettleAttendances(
            $this->createStub(AttendanceRepository::class),
            $this->createStub(EventDispatcherInterface::class)
        );
        $component->debtorId = \Symfony\Component\Uid\Uuid::v7()->toBase58();
        $component->items = [
            ['amount' => 10.0, 'type' => 'attendance', 'attendanceId' => 1],
            ['amount' => -2.0, 'type' => 'discount', 'attendanceId' => null],
        ];

        $debtor = $this->createMock(Debtor::class);
        $debtor->method('getBalance')->willReturn(0);

        $debtorRepository = $this->createStub(DebtorRepository::class);
        $debtorRepository->method('findOneBy')->willReturn($debtor);

        $component->onBalanceChanged($debtorRepository);

        $discountItems = array_filter($component->items, fn ($i) => 'discount' === ($i['type'] ?? ''));
        $this->assertCount(0, $discountItems);
        $this->assertCount(1, $component->items);
    }

    public function testOnBalanceChangedAddsDiscountItemWhenNoExistingDiscountAndBalancePositive(): void
    {
        $component = new SettleAttendances(
            $this->createStub(AttendanceRepository::class),
            $this->createStub(EventDispatcherInterface::class)
        );
        $component->debtorId = \Symfony\Component\Uid\Uuid::v7()->toBase58();
        $component->items = [
            ['amount' => 10.0, 'type' => 'attendance', 'attendanceId' => 1],
        ];

        $debtor = $this->createMock(Debtor::class);
        $debtor->method('getBalance')->willReturn(300); // 3.00 EUR in cents

        $debtorRepository = $this->createStub(DebtorRepository::class);
        $debtorRepository->method('findOneBy')->willReturn($debtor);

        $component->onBalanceChanged($debtorRepository);

        $this->assertCount(2, $component->items);
        $discountItems = array_values(array_filter($component->items, fn ($i) => 'discount' === ($i['type'] ?? '')));
        $this->assertCount(1, $discountItems);
        $this->assertEqualsWithDelta(-3.0, $discountItems[0]['amount'], 0.001);
    }

    public function testGetTotalAmountExcludesDiscountWhenUseBalanceFalse(): void
    {
        $component = new SettleAttendances(
            $this->createStub(AttendanceRepository::class),
            $this->createStub(EventDispatcherInterface::class)
        );
        $component->useBalance = false;
        $component->items = [
            ['amount' => 10.0, 'type' => 'attendance'],
            ['amount' => -5.0, 'type' => 'discount'],
        ];

        $this->assertEqualsWithDelta(10.0, $component->getTotalAmount(), 0.001);
    }

    public function testGetTotalAmountIncludesDiscountWhenUseBalanceTrue(): void
    {
        $component = new SettleAttendances(
            $this->createStub(AttendanceRepository::class),
            $this->createStub(EventDispatcherInterface::class)
        );
        $component->useBalance = true;
        $component->items = [
            ['amount' => 10.0, 'type' => 'attendance'],
            ['amount' => -5.0, 'type' => 'discount'],
        ];

        $this->assertEqualsWithDelta(5.0, $component->getTotalAmount(), 0.001);
    }
}
