<?php

declare(strict_types=1);

namespace Ferienpass\AdminBundle\Tests\Dto;

use Doctrine\Common\Collections\ArrayCollection;
use Ferienpass\AdminBundle\Dto\BillingAddressDto;
use Ferienpass\CoreBundle\Entity\Attendance;
use Ferienpass\CoreBundle\Entity\Debtor;
use Ferienpass\CoreBundle\Entity\Offer\Base;
use Ferienpass\CoreBundle\Entity\Payment;
use Ferienpass\CoreBundle\Entity\PaymentItem;
use Ferienpass\CoreBundle\Entity\PostalAddress;
use Ferienpass\CoreBundle\Entity\User;
use PHPUnit\Framework\TestCase;

final class BillingAddressDtoTest extends TestCase
{
    public function testIsPaidDefaultsFalse(): void
    {
        $dto = new BillingAddressDto();

        self::assertFalse($dto->isPaid());
    }

    public function testIsPaidAfterSet(): void
    {
        $dto = new BillingAddressDto();
        $dto->paid = true;

        self::assertTrue($dto->isPaid());
    }

    public function testFromDebtorWithNoUserReturnsEmptyDto(): void
    {
        $debtor = new Debtor();

        $dto = BillingAddressDto::fromDebtor($debtor);

        self::assertNull($dto->address);
        self::assertNull($dto->email);
    }

    public function testFromDebtorWithUserAndNoAddressUsesName(): void
    {
        $user = new User();
        $user->setFirstname('John');
        $user->setLastname('Doe');

        $debtor = new Debtor($user);

        $dto = BillingAddressDto::fromDebtor($debtor);

        self::assertStringContainsString('John', $dto->address ?? '');
        self::assertStringContainsString('Doe', $dto->address ?? '');
    }

    public function testFromDebtorWithUserAndPostalAddressIncludesAddress(): void
    {
        $address = new PostalAddress();
        $address->setStreet('Main Street 1');
        $address->setPostalCode('12345');
        $address->setCity('Testville');

        $user = new User();
        $user->setFirstname('Jane');
        $user->setLastname('Smith');
        $user->setEmail('jane@example.com');
        $user->addPostalAddress($address);

        $debtor = new Debtor($user);

        $dto = BillingAddressDto::fromDebtor($debtor);

        self::assertStringContainsString('Main Street 1', $dto->address ?? '');
        self::assertStringContainsString('12345', $dto->address ?? '');
        self::assertStringContainsString('Testville', $dto->address ?? '');
        self::assertSame('jane@example.com', $dto->email);
    }

    public function testFromPaymentPopulatesFromDebtor(): void
    {
        $user = new User();
        $user->setEmail('test@example.com');
        $debtor = new Debtor($user);
        $payment = new Payment($debtor);

        $dto = BillingAddressDto::fromPayment($payment);

        self::assertSame('test@example.com', $dto->email);
        self::assertInstanceOf(\Doctrine\Common\Collections\Collection::class, $dto->items);
    }

    public function testFromPaymentThrowsWhenNoDebtor(): void
    {
        $payment = new Payment(new Debtor());

        $rp = new \ReflectionProperty($payment, 'debtor');
        $rp->setValue($payment, null);

        $this->expectException(\BadMethodCallException::class);
        BillingAddressDto::fromPayment($payment);
    }

    public function testToPaymentSetsBillingAddressAndEmail(): void
    {
        $user = new User();
        $user->setEmail('billing@example.com');
        $debtor = new Debtor($user);
        $payment = new Payment($debtor);

        $dto = new BillingAddressDto();
        $dto->address = '123 Main St';
        $dto->email = 'billing@example.com';
        $dto->items = new ArrayCollection();

        $result = $dto->toPayment($payment);

        self::assertSame('123 Main St', $result->getBillingAddress());
        self::assertSame('billing@example.com', $result->getBillingEmail());
    }

    public function testToPaymentAddsItemsToPayment(): void
    {
        $user = new User();
        $debtor = new Debtor($user);
        $payment = new Payment($debtor);

        $offer = new Base();
        $attendance = new Attendance($offer, null);
        $existingItem = new PaymentItem(null, 500, $attendance);

        $dto = new BillingAddressDto();
        $dto->address = 'Addr';
        $dto->email = null;
        $dto->items = new ArrayCollection([$existingItem]);

        $result = $dto->toPayment($payment);

        self::assertCount(1, $result->getItems());
    }
}
