<?php

declare(strict_types=1);

namespace Dealerdirect\Agreements\Tests\Mailers;

use Dealerdirect\Agreements\DataObjects\Agent;
use Dealerdirect\Agreements\Enums\Locale;
use Dealerdirect\Agreements\Mailers\SalesAgreementMailer;
use Dealerdirect\Agreements\Tests\TestCase;
use Dealerdirect\Agreements\Tests\Traits\DataObjectFixtures;
use DealerDirect\Generic\Category\Site;
use DealerDirect\Generic\Category\VehicleType;
use DealerDirect\Sdk\Facade\Email as EmailClient;
use Dealerdirect\Sdk\Model\Email\Attachment;
use Dealerdirect\Sdk\Model\Email\Email;
use Dealerdirect\Sdk\Model\Email\SubstitutionKeys;

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

    private const DEFAULT_SUBSTITUTIONS = [
        SubstitutionKeys::CONSUMER_ADDRESS => 'Main Street 123, 1234 AB, Amsterdam, Nederland',
        SubstitutionKeys::CONSUMER_EMAIL => 'a.b@c.de',
        SubstitutionKeys::CONSUMER_PHONE => '+31612345678',

        SubstitutionKeys::DEALER_ADDRESS => 'Sub Street 123, 1234 AB, Amsterdam, Nederland',
        SubstitutionKeys::DEALER_EMAIL => 'autoboer@joejoe.nl',
        SubstitutionKeys::DEALER_NAME => 'Autoboer B.V.',
        SubstitutionKeys::DEALER_PHONE => '+31612345678',

        SubstitutionKeys::FIRST_NAME => 'Sjaak',
        SubstitutionKeys::LAST_NAME => 'Afhaak',
        SubstitutionKeys::LOT_NUMBER => 12345,
        SubstitutionKeys::VEHICLE_MAKE => 'Volkswagen',
        SubstitutionKeys::VEHICLE_MODEL => 'Golf',
    ];

    public function test_method_create(): void
    {
        /** @var EmailClient $emailClient */
        $emailClient = $this->createStub(EmailClient::class);
        $mailer = SalesAgreementMailer::create(
            $emailClient,
            $this->fixtureSalesAgreement(),
            'pdf-content',
            ['carNl' => 'ikwilvanmijnautoaf.nl'],
            'EmsbBUFwIbL2Sq2vBj519AyE8Gazb2Z6nUOPHPQPnX4'
        );

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

    public function test_method_get_email(): void
    {
        $email = SalesAgreementMailer::getEmail(
            $this->fixtureSalesAgreement(),
            'pdf-content',
            ['carNl' => 'ikwilvanmijnautoaf.nl'],
            'EmsbBUFwIbL2Sq2vBj519AyE8Gazb2Z6nUOPHPQPnX4'
        );

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

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

        $mailer = new SalesAgreementMailer($emailClient, $email, $this->fixtureSalesAgreement());

        $recipients = $mailer->getRecipients();

        $this->assertCount(1, $recipients);
        $this->assertSame('a.b@c.de', $recipients[0]->getEmailAddress());
        $this->assertSame('Sjaak Afhaak', $recipients[0]->getName());
    }

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

        $mailer = new SalesAgreementMailer($emailClient, $email, $this->fixtureSalesAgreement());

        $cc = $mailer->getCc();

        $this->assertCount(0, $cc);
    }

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

        $mailer = new SalesAgreementMailer($emailClient, $email, $this->fixtureSalesAgreement());

        $bcc = $mailer->getBcc();

        $this->assertCount(0, $bcc);
    }

    public function test_method_get_bcc_as_motor(): void
    {
        $salesAgreement = $this->fixtureSalesAgreement(vehicleType: VehicleType::MOTOR);

        /** @var EmailClient $emailClient */
        $emailClient = $this->createStub(EmailClient::class);
        /** @var Email $email */
        $email = $this->createStub(Email::class);

        $mailer = new SalesAgreementMailer($emailClient, $email, $salesAgreement);

        $bcc = $mailer->getBcc();

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

    /**
     * @dataProvider provideAdditionalAttachments
     *
     * @param mixed[] $dataOverride
     */
    public function test_get_email_has_correct_attachments(array $dataOverride, string $expectedFile): void
    {
        $salesAgreement = $this->fixtureSalesAgreement(...$dataOverride);

        $email = SalesAgreementMailer::getEmail(
            $salesAgreement,
            'pdf-content',
            [
                'carNl' => 'ikwilvanmijnautoaf.nl',
                'marktplaatsNl' => 'marktplaats.nl',
            ],
            'EmsbBUFwIbL2Sq2vBj519AyE8Gazb2Z6nUOPHPQPnX4'
        );

        /** @var Attachment[] $attachments */
        $attachments = $email->getAttachments();

        $this->assertCount(2, $attachments);

        $this->assertSame('Verkoopovereenkomst_12345.pdf', $attachments[0]->getFileName());
        $this->assertSame('pdf-content', $attachments[0]->getContent());

        $this->assertSame('Documenten_Overdracht.pdf', $attachments[1]->getFileName());
        $this->assertSame(
            file_get_contents(dirname(__DIR__, 2) . "/resources/files/$expectedFile"),
            $attachments[1]->getContent()
        );
    }

    /**
     * @dataProvider provideAdditionalSubstitutions
     *
     * @param mixed[]                 $dataOverride
     * @param array<string|int,mixed> $customSubstitutions
     */
    public function test_get_email_has_correct_substitutions(array $dataOverride, array $customSubstitutions): void
    {
        $salesAgreement = $this->fixtureSalesAgreement(...$dataOverride);

        $email = SalesAgreementMailer::getEmail(
            $salesAgreement,
            'pdf-content',
            [
                'carNl' => 'car.nl',
                'motorDe' => 'motor.de',
                'marktplaatsNl' => 'marktplaats.nl',
            ],
            'EmsbBUFwIbL2Sq2vBj519AyE8Gazb2Z6nUOPHPQPnX4'
        );

        $substitutions = $email->getSubstitution();

        foreach (array_merge(self::DEFAULT_SUBSTITUTIONS, $customSubstitutions) as $key => $value) {
            $this->assertArrayHasKey($key, $substitutions);
            $this->assertSame($value, $substitutions[$key]);
        }
    }

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

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

        $mailer = new SalesAgreementMailer($emailClient, $email, $this->fixtureSalesAgreement());

        $mailer->send();
    }

    /**
     * @covers \Dealerdirect\Agreements\Exceptions\EmptyRecipientsException
     */
    public function test_method_send_should_throw_exception_when_recipients_are_empty(): void
    {
        $this->expectException(\Dealerdirect\Agreements\Exceptions\EmptyRecipientsException::class);

        $email = $this->createStub(Email::class);

        $emailClient = $this->createStub(EmailClient::class);

        /** @var EmailClient $emailClient */
        $emailClient = $this->createStub(EmailClient::class);
        /** @var Email $email */
        $email = $this->createStub(Email::class);

        $mailer = new SalesAgreementMailer($emailClient, $email, $this->fixtureSalesAgreement(
            consumer: $this->fixtureConsumer(emails: []),
        ));

        $mailer->send();
    }

    /**
     * @return array<string,array{0:array<string,string>,1:array<string,string>}>
     */
    public static function provideAdditionalSubstitutions(): array
    {
        return [
            'default' => [[], []],
            'Marktplaats without agent' => [
                [
                    'siteReference' => Site::REF_CAR_MARKTPLAATS_NL,
                    'salesAgent' => new Agent(0, '-'),
                ],
                [
                    SubstitutionKeys::AGENT_PHONE => '085 - 77 33 443',
                    SubstitutionKeys::AGENT_NAME => 'Het Marktplaats team',
                ],
            ],
            'Marktplaats with agent' => [
                ['siteReference' => Site::REF_CAR_MARKTPLAATS_NL],
                [
                    SubstitutionKeys::AGENT_PHONE => '085 - 77 33 443',
                    SubstitutionKeys::AGENT_NAME => 'Benny Doof',
                ],
            ],
            'Motorbikes' => [
                ['vehicleType' => VehicleType::MOTOR],
                [SubstitutionKeys::AGENT_PHONE => '053 - 20 844 49'],
            ],
            'Motorbikes DE' => [
                [
                    'vehicleType' => VehicleType::MOTOR,
                    'locale' => Locale::DE_DE,
                    'siteReference' => Site::REF_MOTO_DE,
                ],
                [
                    SubstitutionKeys::AGENT_PHONE => '02131 - 7 51 25 28',
                    SubstitutionKeys::VEHICLE_CONSUMER_PRICE => 7500,
                    SubstitutionKeys::SALUTATION => '',
                ],
            ],
        ];
    }

    /**
     * @return array<array{0:array<string,string>,1:string}>
     */
    public static function provideAdditionalAttachments(): array
    {
        return [
            [['vehicleType' => VehicleType::SCOOTER], 'SD-00414_Kentekendocumenten_Overdracht_ScooterNL.pdf'],
            [['vehicleType' => VehicleType::MOTOR], 'SD-00414_Kentekendocumenten_Overdracht.pdf'],
            [['siteReference' => Site::REF_CAR_MARKTPLAATS_NL], 'MP_000001_overdracht.pdf'],
        ];
    }
}
