<?php

declare(strict_types=1);

namespace Dealerdirect\Agreements\Tests\Mailers;

use Dealerdirect\Agreements\DataObjects\Contact;
use Dealerdirect\Agreements\Enums\Locale;
use Dealerdirect\Agreements\Exceptions\EmptyRecipientsException;
use Dealerdirect\Agreements\Mailers\PurchaseAgreementMailer;
use Dealerdirect\Agreements\Tests\TestCase;
use Dealerdirect\Agreements\Tests\Traits\DataObjectFixtures;
use DealerDirect\Generic\Category\VehicleType;
use DealerDirect\Generic\Category\VehicleTypeCarBodyType;
use DealerDirect\Sdk\Facade\Email as EmailClient;
use Dealerdirect\Sdk\Model\Email\Email;
use Dealerdirect\Sdk\Model\Email\Recipient;
use Dealerdirect\Sdk\Model\Email\SubstitutionKeys;

/**
 * @covers \Dealerdirect\Agreements\Mailers\PurchaseAgreementMailer
 * @covers \Dealerdirect\Agreements\Mailers\Mailer
 *
 * @uses \Dealerdirect\Agreements\DataObjects\Contact
 * @uses \Dealerdirect\Agreements\DataObjects\PurchaseAgreement
 * @uses \Dealerdirect\Agreements\Utils\GeneralUtils
 * @uses \Dealerdirect\Agreements\Utils\Translator
 * @uses \Dealerdirect\Agreements\DataObjects\Vehicle
 * @uses \Dealerdirect\Agreements\DataObjects\Conditions
 */
final class PurchaseAgreementMailerTest extends TestCase
{
    use DataObjectFixtures;

    public function test_method_create(): void
    {
        /** @var EmailClient $emailClient */
        $emailClient = $this->createStub(EmailClient::class);

        $mailer = PurchaseAgreementMailer::create(
            $emailClient,
            self::fixturePurchaseAgreement(),
            'pdf-content'
        );

        $this->assertInstanceOf(PurchaseAgreementMailer::class, $mailer);
    }

    public function test_method_get_email(): void
    {
        $email = PurchaseAgreementMailer::getEmail(
            self::fixturePurchaseAgreement(),
            'pdf-content'
        );

        $this->assertInstanceOf(Email::class, $email);
    }

    /**
     * @dataProvider provideValidRecipientsData
     *
     * @param array<string> $expectedEmails
     */
    public function test_method_get_recipients_returns_valid_emails(Contact $company, array $expectedEmails): void
    {
        /** @var EmailClient $emailClient */
        $emailClient = $this->createStub(EmailClient::class);
        $data = self::fixturePurchaseAgreement(company: $company);

        $mailer = new PurchaseAgreementMailer(
            $emailClient,
            PurchaseAgreementMailer::getEmail($data, 'pdf-content'),
            $data
        );

        $recipients = $mailer->getRecipients();

        $this->assertNotEmpty($recipients);
        foreach ($recipients as $recipient) {
            $this->assertInstanceOf(Recipient::class, $recipient);
            $this->assertContains($recipient->getEmailAddress(), $expectedEmails);
            $this->assertSame($company->getName(), $recipient->getName());
        }
    }

    public function test_method_get_recipients_returns_empty_array_when_no_valid_emails(): void
    {
        /** @var EmailClient $emailClient */
        $emailClient = $this->createStub(EmailClient::class);
        $data = self::fixturePurchaseAgreement(company: self::fixtureCompany(['invalid-email']));

        $mailer = new PurchaseAgreementMailer(
            $emailClient,
            PurchaseAgreementMailer::getEmail($data, 'pdf-content'),
            $data
        );

        $recipients = $mailer->getRecipients();

        $this->assertEmpty($recipients);
    }

    public function test_method_get_cc_as_default(): void
    {
        /** @var EmailClient $emailClient */
        $emailClient = $this->createStub(EmailClient::class);
        $mailer = new PurchaseAgreementMailer(
            $emailClient,
            PurchaseAgreementMailer::getEmail(self::fixturePurchaseAgreement(), 'pdf-content'),
            self::fixturePurchaseAgreement()
        );

        $cc = $mailer->getCc();

        $this->assertEmpty($cc);
    }

    public function test_method_get_bcc_as_motor(): void
    {
        /** @var EmailClient $emailClient */
        $emailClient = $this->createStub(EmailClient::class);
        $data = self::fixturePurchaseAgreement(vehicleType: VehicleType::MOTOR);

        $mailer = new PurchaseAgreementMailer(
            $emailClient,
            PurchaseAgreementMailer::getEmail($data, 'pdf-content'),
            $data
        );

        $bcc = $mailer->getBcc();

        $this->assertCount(1, $bcc);
        $this->assertInstanceOf(Recipient::class, $bcc[0]);
        $this->assertSame('Kerim Gilbers', $bcc[0]->getName());
        $this->assertSame('kerim@dealerdirect.nl', $bcc[0]->getEmailAddress());
    }

    public function test_method_get_bcc_as_nl_and_big_fee(): void
    {
        /** @var EmailClient $emailClient */
        $emailClient = $this->createStub(EmailClient::class);
        $data = self::fixturePurchaseAgreement(locale: Locale::NL_NL, salesFee: 800);

        $mailer = new PurchaseAgreementMailer(
            $emailClient,
            PurchaseAgreementMailer::getEmail($data, 'pdf-content'),
            $data
        );

        $bcc = $mailer->getBcc();

        $this->assertCount(1, $bcc);
        $this->assertInstanceOf(Recipient::class, $bcc[0]);
        $this->assertSame($data->company->getName(), $bcc[0]->getName());
        $this->assertSame('feedback@ikwilvanmijnautoaf.nl', $bcc[0]->getEmailAddress());
    }

    public function test_method_get_bcc_as_default(): void
    {
        /** @var EmailClient $emailClient */
        $emailClient = $this->createStub(EmailClient::class);
        $data = self::fixturePurchaseAgreement(locale: Locale::NL_NL, salesFee: 450);

        $mailer = new PurchaseAgreementMailer(
            $emailClient,
            PurchaseAgreementMailer::getEmail($data, 'pdf-content'),
            $data
        );

        $bcc = $mailer->getBcc();

        $this->assertEmpty($bcc);
    }

    public function test_get_email_has_correct_attachments(): void
    {
        $data = self::fixturePurchaseAgreement();
        $email = PurchaseAgreementMailer::getEmail($data, 'pdf-content');

        $this->assertNotEmpty($email->getAttachments());
        $this->assertCount(1, $email->getAttachments());
        $this->assertSame('Aankoopspecificatie - kavel 12345.pdf', $email->getAttachments()[0]->getFileName());
        $this->assertSame('pdf-content', $email->getAttachments()[0]->getContent());
        $this->assertSame('application/pdf', $email->getAttachments()[0]->getMimeType());
    }

    public function test_get_email_has_correct_substitutions(): void
    {
        $data = self::fixturePurchaseAgreement(
            vehicle: self::fixtureVehicle(
                constructionYear: 2023,
                bodyType: VehicleTypeCarBodyType::FIVE_DOOR_HATCHBACK,
            ),
        );
        $email = PurchaseAgreementMailer::getEmail($data, 'pdf-content');

        $substitutions = $email->getSubstitution();

        $this->assertNotEmpty($substitutions);

        $this->assertHasKeyAndValue([
            SubstitutionKeys::AGENT_NAME => 'Benny Doof',
            SubstitutionKeys::CONSUMER_ADDRESS => 'Main Street 123, 1234 AB, Amsterdam, Nederland',
            SubstitutionKeys::CONSUMER_NAME => 'Sjaak Afhaak',
            SubstitutionKeys::CONSUMER_EMAIL => 'a.b@c.de',
            SubstitutionKeys::CONSUMER_PHONE => '+31612345678',
            SubstitutionKeys::FULL_NAME => 'Autoboer B.V.',
            SubstitutionKeys::LOT_NUMBER => 12345,
            SubstitutionKeys::VEHICLE_MAKE => 'Volkswagen',
            SubstitutionKeys::VEHICLE_MODEL => 'Golf',
            SubstitutionKeys::VEHICLE_CONSUMER_PRICE => 7500,
            SubstitutionKeys::VEHICLE_DEALER_FEE => 500,
            SubstitutionKeys::VEHICLE_DEALER_PICK_UP => 'ja',
            SubstitutionKeys::VEHICLE_GROSS_PRICE => 8000,
            SubstitutionKeys::VEHICLE_CONSTRUCTION_YEAR => 2023,
            SubstitutionKeys::VEHICLE_BODYWORK => '5-deurs Hatchback',
            SubstitutionKeys::VEHICLE_DELIVERY_DATE => (new \DateTime())->modify('+ 14 days')->format('d-m-Y'),
            SubstitutionKeys::VEHICLE_MILEAGE => 130000,
            SubstitutionKeys::VEHICLE_REGISTRATION_PLATE => 'AB-12-CD',
            SubstitutionKeys::VEHICLE_TRIM_LEVEL => 'Highline',
            SubstitutionKeys::LEAD_CONSUMER_LABEL_NAME => 'Ikwilvanmijnautoaf.nl',
        ], $substitutions);
    }

    /**
     * @covers \Dealerdirect\Agreements\Exceptions\EmptyRecipientsException
     */
    public function test_method_send_will_throw_exception_when_recipients_are_empty(): void
    {
        /** @var EmailClient $emailClient */
        $emailClient = $this->createStub(EmailClient::class);
        $data = self::fixturePurchaseAgreement(company: self::fixtureCompany(['invalid-email']));

        $mailer = new PurchaseAgreementMailer(
            $emailClient,
            PurchaseAgreementMailer::getEmail($data, 'pdf-content'),
            $data
        );

        $this->expectException(EmptyRecipientsException::class);

        $mailer->send();
    }

    public function test_method_send(): void
    {
        /** @var Email|\PHPUnit\Framework\MockObject\MockObject $email */
        $email = $this->createStub(Email::class);

        /** @var EmailClient|\PHPUnit\Framework\MockObject\MockObject $emailClient */
        $emailClient = $this->createMock(EmailClient::class);
        $emailClient->expects($this->once())->method('sendEmail');

        $mailer = new PurchaseAgreementMailer(
            $emailClient,
            $email,
            self::fixturePurchaseAgreement()
        );

        $mailer->send();
    }

    /**
     * @return array<string,array{Contact,array<string>}>
     */
    public static function provideValidRecipientsData(): array
    {
        return [
            '1 valid email' => [
                self::fixtureCompany(),
                ['autoboer@joejoe.nl'],
            ],
            '2 valid emails' => [
                self::fixtureCompany(['auto@boer1.nl', 'auto@boer2.nl']),
                ['auto@boer1.nl', 'auto@boer2.nl'],
            ],
            '1 invalid email' => [
                self::fixtureCompany(['auto@boer2.nl', 'invalid-email']),
                ['auto@boer2.nl'],
            ],
        ];
    }
}
