<?php
/**
 * Copyright 2015 Adobe
 * All Rights Reserved.
 */
declare(strict_types=1);

namespace Magento\Customer\Model\AccountManagement;

use Magento\Customer\Api\AccountManagementInterface;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Customer\Api\Data\CustomerInterface;
use Magento\Customer\Api\Data\CustomerInterfaceFactory;
use Magento\Customer\Model\Customer;
use Magento\Customer\Model\CustomerFactory;
use Magento\Customer\Model\EmailNotification;
use Magento\Email\Model\ResourceModel\Template\CollectionFactory as TemplateCollectionFactory;
use Magento\Framework\Api\DataObjectHelper;
use Magento\Framework\Api\ExtensibleDataObjectConverter;
use Magento\Framework\Api\SimpleDataObjectConverter;
use Magento\Framework\App\Config\MutableScopeConfigInterface;
use Magento\Framework\Encryption\EncryptorInterface;
use Magento\Framework\Exception\InputException;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\State\InputMismatchException;
use Magento\Framework\Math\Random;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Validator\Exception;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\Helper\Xpath;
use Magento\TestFramework\Mail\Template\TransportBuilderMock;
use PHPUnit\Framework\TestCase;

/**
 * Tests for customer creation via customer account management service.
 *
 * @magentoDbIsolation enabled
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
 */
class CreateAccountTest extends TestCase
{
    /**
     * @var ObjectManagerInterface
     */
    private $objectManager;

    /**
     * @var AccountManagementInterface
     */
    private $accountManagement;

    /**
     * @var CustomerInterfaceFactory
     */
    private $customerFactory;

    /**
     * @var DataObjectHelper
     */
    private $dataObjectHelper;

    /**
     * @var array
     */
    private $defaultCustomerData = [
        'email' => 'customer@example.com',
        'firstname' => 'First name',
        'lastname' => 'Last name',
    ];

    /**
     * @var TransportBuilderMock
     */
    private $transportBuilderMock;

    /**
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @var CustomerRepositoryInterface
     */
    private $customerRepository;

    /**
     * @var ExtensibleDataObjectConverter
     */
    private $extensibleDataObjectConverter;

    /**
     * @var CustomerFactory
     */
    private $customerModelFactory;

    /**
     * @var Random
     */
    private $random;

    /**
     * @var EncryptorInterface
     */
    private $encryptor;

    /**
     * @var MutableScopeConfigInterface
     */
    private $mutableScopeConfig;

    /**
     * @var TemplateCollectionFactory
     */
    private $templateCollectionFactory;

    /**
     * @inheritdoc
     */
    protected function setUp(): void
    {
        $this->objectManager = Bootstrap::getObjectManager();
        $this->accountManagement = $this->objectManager->get(AccountManagementInterface::class);
        $this->customerFactory = $this->objectManager->get(CustomerInterfaceFactory::class);
        $this->dataObjectHelper = $this->objectManager->create(DataObjectHelper::class);
        $this->transportBuilderMock = $this->objectManager->get(TransportBuilderMock::class);
        $this->storeManager = $this->objectManager->get(StoreManagerInterface::class);
        $this->customerRepository = $this->objectManager->get(CustomerRepositoryInterface::class);
        $this->extensibleDataObjectConverter = $this->objectManager->get(ExtensibleDataObjectConverter::class);
        $this->customerModelFactory = $this->objectManager->get(CustomerFactory::class);
        $this->random = $this->objectManager->get(Random::class);
        $this->encryptor = $this->objectManager->get(EncryptorInterface::class);
        $this->mutableScopeConfig = $this->objectManager->get(MutableScopeConfigInterface::class);
        $this->templateCollectionFactory = $this->objectManager->get(TemplateCollectionFactory::class);
        parent::setUp();
    }

    /**
     * @inheritdoc
     */
    protected function tearDown(): void
    {
        parent::tearDown();
        $this->mutableScopeConfig->clean();
    }

    /**
     * @dataProvider createInvalidAccountDataProvider
     * @param array $customerData
     * @param string $password
     * @param string $errorType
     * @param string $errorMessage
     * @return void
     */
    public function testCreateAccountWithInvalidFields(
        array $customerData,
        string $password,
        string $errorType,
        array $errorMessage
    ): void {
        $customerEntity = $this->populateCustomerEntity($this->defaultCustomerData, $customerData);
        $this->expectException($errorType);
        $this->expectExceptionMessage((string)__(...$errorMessage));
        $this->accountManagement->createAccount($customerEntity, $password);
    }

    /**
     * @return array
     */
    public static function createInvalidAccountDataProvider(): array
    {
        return [
            'empty_firstname' => [
                'customerData' => ['firstname' => ''],
                'password' => '_aPassword1',
                'errorType' =>  Exception::class,
                'errorMessage' => ['"%1" is a required value.', 'First Name'],
            ],
            'empty_lastname' => [
                'customerData' => ['lastname' => ''],
                'password' => '_aPassword1',
                'errorType' =>  Exception::class,
                'errorMessage' => ['"%1" is a required value.', 'Last Name'],
            ],
            'empty_email' => [
                'customerData' => ['email' => ''],
                'password' => '_aPassword1',
                'errorType' => Exception::class,
                'errorMessage' => ['The customer email is missing. Enter and try again.'],
            ],
            'invalid_email' => [
                'customerData' => ['email' => 'zxczxczxc'],
                'password' => '_aPassword1',
                'errorType' => Exception::class,
                'errorMessage' => ['"%1" is not a valid email address.', 'Email'],
            ],
            'empty_password' => [
                'customerData' => [],
                'password' => '',
                'errorType' => InputException::class,
                'errorMessage' => ['The password needs at least 8 characters. Create a new password and try again.'],
            ],
            'invalid_password_minimum_length' => [
                'customerData' => [],
                'password' => 'test',
                'errorType' => InputException::class,
                'errorMessage' => ['The password needs at least 8 characters. Create a new password and try again.'],
            ],
            'invalid_password_maximum_length' => [
                'customerData' => [],
                'password' => self::getRandomNumericString(257),
                'errorType' => InputException::class,
                'errorMessage' => ['Please enter a password with at most 256 characters.'],
            ],
            'invalid_password_without_minimum_characters_classes' => [
                'customerData' => [],
                'password' => 'test_password',
                'errorType' => InputException::class,
                'errorMessage' => [
                    'Minimum of different classes of characters in password is %1.'
                    . ' Classes of characters: Lower Case, Upper Case, Digits, Special Characters.',
                    3,
                ],
            ],
            'password_same_as_email' => [
                'customerData' => ['email' => 'test1@test.com'],
                'password' => 'test1@test.com',
                'errorType' => LocalizedException::class,
                'errorMessage' => [
                    'The password can\'t be the same as the email address. Create a new password and try again.',
                ],
            ],
            'send_email_store_id_not_match_website' => [
                'customerData' => [
                    CustomerInterface::WEBSITE_ID => 1,
                    CustomerInterface::STORE_ID => 5,
                ],
                'password' => '_aPassword1',
                'errorType' => LocalizedException::class,
                'errorMessage' => [
                    'The store view is not in the associated website.',
                ],
            ],
        ];
    }

    /**
     * @magentoAppArea frontend
     * @magentoDataFixture Magento/Customer/_files/customer_welcome_email_template.php
     * @return void
     */
    public function testCreateAccountWithConfiguredWelcomeEmail(): void
    {
        $emailTemplate = $this->getCustomTemplateId('customer_create_account_email_template');
        $this->setConfig([EmailNotification::XML_PATH_REGISTER_EMAIL_TEMPLATE => $emailTemplate,]);
        $this->accountManagement->createAccount(
            $this->populateCustomerEntity($this->defaultCustomerData),
            '_Password1'
        );
        $this->assertEmailData(
            [
                'name' => 'Owner',
                'email' => 'owner@example.com',
                'message' => 'Customer create account email template',
            ]
        );
    }

    /**
     * @magentoAppArea frontend
     * @magentoDataFixture Magento/Customer/_files/customer_welcome_no_password_email_template.php
     * @magentoConfigFixture current_store customer/create_account/email_identity support
     * @return void
     */
    public function testCreateAccountWithConfiguredWelcomeNoPasswordEmail(): void
    {
        $emailTemplate = $this->getCustomTemplateId('customer_create_account_email_no_password_template');
        $this->setConfig([EmailNotification::XML_PATH_REGISTER_NO_PASSWORD_EMAIL_TEMPLATE => $emailTemplate,]);
        $this->accountManagement->createAccount($this->populateCustomerEntity($this->defaultCustomerData));
        $this->assertEmailData(
            [
                'name' => 'CustomerSupport',
                'email' => 'support@example.com',
                'message' => 'Customer create account email no password template',
            ]
        );
    }

    /**
     * @magentoAppArea frontend
     * @magentoDataFixture Magento/Customer/_files/customer_confirmation_email_template.php
     * @magentoConfigFixture current_website customer/create_account/confirm 1
     * @magentoConfigFixture current_store customer/create_account/email_identity custom1
     * @return void
     */
    public function testCreateAccountWithConfiguredConfirmationEmail(): void
    {
        $emailTemplate = $this->getCustomTemplateId('customer_create_account_email_confirmation_template');
        $this->setConfig([EmailNotification::XML_PATH_CONFIRM_EMAIL_TEMPLATE => $emailTemplate,]);
        $this->accountManagement->createAccount(
            $this->populateCustomerEntity($this->defaultCustomerData),
            '_Password1'
        );
        $this->assertEmailData(
            [
                'name' => 'Custom 1',
                'email' => 'custom1@example.com',
                'message' => 'Customer create account email confirmation template',
            ]
        );
    }

    /**
     * @magentoAppArea frontend
     * @magentoDataFixture Magento/Customer/_files/customer_confirmed_email_template.php
     * @magentoConfigFixture current_store customer/create_account/email_identity custom1
     * @magentoConfigFixture current_website customer/create_account/confirm 1
     * @return void
     */
    public function testCreateAccountWithConfiguredConfirmedEmail(): void
    {
        $emailTemplate = $this->getCustomTemplateId('customer_create_account_email_confirmed_template');
        $this->setConfig([EmailNotification::XML_PATH_CONFIRMED_EMAIL_TEMPLATE => $emailTemplate,]);
        $this->accountManagement->createAccount(
            $this->populateCustomerEntity($this->defaultCustomerData),
            '_Password1'
        );
        $customer = $this->customerRepository->get('customer@example.com');
        $this->accountManagement->activate($customer->getEmail(), $customer->getConfirmation());
        $this->assertEmailData(
            [
                'name' => 'Custom 1',
                'email' => 'custom1@example.com',
                'message' => 'Customer create account email confirmed template',
            ]
        );
    }

    /**
     * Assert that when you create customer account via admin, link with "set password" is send to customer email.
     *
     * @return void
     */
    public function testSendEmailWithSetPasswordLink(): void
    {
        $customerEntity = $this->populateCustomerEntity($this->defaultCustomerData);
        $newCustomerEntity = $this->accountManagement->createAccount($customerEntity);
        $mailTemplate = $this->transportBuilderMock->getSentMessage()->getBody()->bodyToString();

        $this->assertEquals(
            1,
            Xpath::getElementsCountForXpath(
                sprintf("//a[contains(@href, 'customer/account/createPassword/?id=%s')]", $newCustomerEntity->getId()),
                quoted_printable_decode($mailTemplate)
            ),
            'Password creation link was not found.'
        );
    }

    /**
     * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
     * @return void
     */
    public function testCreateCustomerOnSecondWebsite(): void
    {
        $customerData = [
            CustomerInterface::WEBSITE_ID => $this->storeManager->getWebsite('test')->getId(),
            CustomerInterface::STORE_ID => $this->storeManager->getStore('fixture_third_store')->getId(),
        ];
        $expectedCustomerData = array_merge($this->defaultCustomerData, $customerData);
        $customerEntity = $this->populateCustomerEntity($this->defaultCustomerData, $customerData);
        $savedCustomerEntity = $this->accountManagement->createAccount($customerEntity);

        $this->assertNotNull($savedCustomerEntity->getId());
        $this->assertCustomerData($savedCustomerEntity, $expectedCustomerData);
    }

    /**
     * @return void
     */
    public function testCreateNewCustomerWithPasswordHash(): void
    {
        $customerData = $expectedCustomerData = [
            CustomerInterface::EMAIL => 'email@example.com',
            CustomerInterface::STORE_ID => 1,
            CustomerInterface::FIRSTNAME => 'Tester',
            CustomerInterface::LASTNAME => 'McTest',
            CustomerInterface::GROUP_ID => 1,
        ];
        $newCustomerEntity = $this->populateCustomerEntity($customerData);
        $password = $this->random->getRandomString(8);
        $passwordHash = $this->encryptor->getHash($password, true);
        $savedCustomer = $this->accountManagement->createAccountWithPasswordHash(
            $newCustomerEntity,
            $passwordHash
        );
        $this->assertNotNull($savedCustomer->getId());
        $this->assertCustomerData($savedCustomer, $expectedCustomerData);
        $this->assertEmpty($savedCustomer->getSuffix());
        $this->assertEquals(
            $savedCustomer->getId(),
            $this->accountManagement->authenticate($customerData[CustomerInterface::EMAIL], $password)->getId()
        );
    }

    /**
     * Customer has two addresses one of it is allowed in website and second is not
     *
     * @magentoDataFixture Magento/Customer/_files/customer.php
     * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php
     * @magentoDataFixture Magento/Store/_files/websites_different_countries.php
     * @magentoConfigFixture fixture_second_store_store general/country/allow UA
     * @return void
     */
    public function testCreateNewCustomerWithPasswordHashWithNotAllowedCountry(): void
    {
        $customerId = 1;
        $allowedCountryIdForSecondWebsite = 'UA';
        $store = $this->storeManager->getStore('fixture_second_store');
        $customerData = $this->customerRepository->getById($customerId);
        $customerData->getAddresses()[1]->setRegion(null)->setCountryId($allowedCountryIdForSecondWebsite)
            ->setRegionId(null);
        $customerData->getAddresses()[1]->setIsDefaultBilling(true);
        $customerData->getAddresses()[1]->setIsDefaultShipping(true);
        foreach ($customerData->getAddresses() as $address) {
            $address->setId(null);
            $address->setCustomerId(null);
        }
        $customerData->setDefaultBilling(null);
        $customerData->setDefaultShipping(null);
        $customerData->setStoreId($store->getId())->setWebsiteId($store->getWebsiteId())->setId(null);
        $password = $this->random->getRandomString(8);
        $passwordHash = $this->encryptor->getHash($password, true);
        $savedCustomer = $this->accountManagement->createAccountWithPasswordHash(
            $customerData,
            $passwordHash
        );
        $this->assertCount(
            1,
            $savedCustomer->getAddresses(),
            'The wrong address quantity was saved'
        );
        $this->assertSame(
            'UA',
            $savedCustomer->getAddresses()[0]->getCountryId(),
            'The address with the disallowed country was saved'
        );
    }

    /**
     * @magentoAppArea frontend
     * @magentoDataFixture Magento/Customer/_files/customer.php
     * @return void
     */
    public function testCreateNoExistingCustomer(): void
    {
        $existingCustId = 1;
        $existingCustomer = $this->customerRepository->getById($existingCustId);
        $customerData = $expectedCustomerData = [
            CustomerInterface::EMAIL => 'savecustomer@example.com',
            CustomerInterface::FIRSTNAME => 'Firstsave',
            CustomerInterface::LASTNAME => 'Lastsave',
            CustomerInterface::ID => null,
        ];
        unset($expectedCustomerData[CustomerInterface::ID]);
        $customerEntity = $this->populateCustomerEntity($existingCustomer->__toArray(), $customerData);
        $customerEntity->setDefaultBilling(null);
        $customerEntity->setDefaultShipping(null);
        $customerEntity->setAddresses([]);

        $customerAfter = $this->accountManagement->createAccount($customerEntity, '_aPassword1');
        $this->assertGreaterThan(0, $customerAfter->getId());
        $this->assertCustomerData($customerAfter, $expectedCustomerData);
        $this->accountManagement->authenticate(
            $customerAfter->getEmail(),
            '_aPassword1'
        );
        $attributesBefore = $this->extensibleDataObjectConverter->toFlatArray(
            $existingCustomer,
            [],
            CustomerInterface::class
        );
        $attributesAfter = $this->extensibleDataObjectConverter->toFlatArray(
            $customerAfter,
            [],
            CustomerInterface::class
        );
        // ignore 'updated_at'
        unset($attributesBefore['updated_at']);
        unset($attributesAfter['updated_at']);
        $inBeforeOnly = array_diff_assoc($attributesBefore, $attributesAfter);
        $inAfterOnly = array_diff_assoc($attributesAfter, $attributesBefore);
        $expectedInBefore = [
            'default_billing',
            'default_shipping',
            'email',
            'firstname',
            'id',
            'lastname',
        ];
        sort($expectedInBefore);
        $actualInBeforeOnly = array_keys($inBeforeOnly);
        sort($actualInBeforeOnly);
        $this->assertEquals($expectedInBefore, $actualInBeforeOnly);
        $expectedInAfter = [
            'created_in',
            'email',
            'firstname',
            'id',
            'lastname',
        ];
        $actualInAfterOnly = array_keys($inAfterOnly);
        foreach ($expectedInAfter as $item) {
            $this->assertContains($item, $actualInAfterOnly);
        }
    }

    /**
     * @return void
     */
    public function testCreateCustomerInServiceVsInModel(): void
    {
        $password = '_aPassword1';
        $firstCustomerData = $secondCustomerData = [
            CustomerInterface::EMAIL => 'email@example.com',
            CustomerInterface::FIRSTNAME => 'Tester',
            CustomerInterface::LASTNAME => 'McTest',
            CustomerInterface::GROUP_ID => 1,
        ];
        $secondCustomerData[CustomerInterface::EMAIL] = 'email2@example.com';

        /** @var Customer $customerModel */
        $customerModel = $this->customerModelFactory->create();
        $customerModel->setData($firstCustomerData)->setPassword($password);
        $customerModel->save();
        /** @var Customer $customerModel */
        $savedModel = $this->customerModelFactory->create()->load($customerModel->getId());
        $dataInModel = $savedModel->getData();
        $newCustomerEntity = $this->populateCustomerEntity($secondCustomerData);

        $customerData = $this->accountManagement->createAccount($newCustomerEntity, $password);
        $this->assertNotNull($customerData->getId());
        $savedCustomer = $this->customerRepository->getById($customerData->getId());

        /** @var SimpleDataObjectConverter $simpleDataObjectConverter */
        $simpleDataObjectConverter = $this->objectManager->get(SimpleDataObjectConverter::class);

        $dataInService = $simpleDataObjectConverter->toFlatArray(
            $savedCustomer,
            CustomerInterface::class
        );
        $expectedDifferences = [
            'created_at',
            'updated_at',
            'email',
            'is_active',
            'entity_id',
            'entity_type_id',
            'password_hash',
            'attribute_set_id',
            'disable_auto_group_change',
            'confirmation',
            'reward_update_notification',
            'reward_warning_notification',
        ];
        foreach ($dataInModel as $key => $value) {
            if (!in_array($key, $expectedDifferences)) {
                if ($value === null) {
                    $this->assertArrayNotHasKey($key, $dataInService);
                } elseif (isset($dataInService[$key])) {
                    $this->assertEquals($value, $dataInService[$key], 'Failed asserting value for ' . $key);
                }
            }
        }
        $this->assertEquals($secondCustomerData[CustomerInterface::EMAIL], $dataInService['email']);
        $this->assertArrayNotHasKey('is_active', $dataInService);
        $this->assertArrayNotHasKey('password_hash', $dataInService);
    }

    /**
     * @return void
     */
    public function testCreateNewCustomer(): void
    {
        $customerData = $expectedCustomerData = [
            CustomerInterface::EMAIL => 'email@example.com',
            CustomerInterface::STORE_ID => 1,
            CustomerInterface::FIRSTNAME => 'Tester',
            CustomerInterface::LASTNAME => 'McTest',
            CustomerInterface::GROUP_ID => 1,
        ];
        $newCustomerEntity = $this->populateCustomerEntity($customerData);

        $savedCustomer = $this->accountManagement->createAccount($newCustomerEntity, '_aPassword1');
        $this->assertNotNull($savedCustomer->getId());
        $this->assertCustomerData($savedCustomer, $expectedCustomerData);
        $this->assertEmpty($savedCustomer->getSuffix());
    }

    /**
     * @magentoAppArea frontend
     * @magentoDataFixture Magento/Customer/_files/customer.php
     * @return void
     */
    public function testCreateNewCustomerFromClone(): void
    {
        $existingCustId = 1;
        $existingCustomer = $this->customerRepository->getById($existingCustId);
        $customerEntity = $this->customerFactory->create();
        $this->dataObjectHelper->mergeDataObjects(
            CustomerInterface::class,
            $customerEntity,
            $existingCustomer
        );
        $customerData = $expectedCustomerData = [
            CustomerInterface::EMAIL => 'savecustomer@example.com',
            CustomerInterface::FIRSTNAME => 'Firstsave',
            CustomerInterface::LASTNAME => 'Lastsave',
            CustomerInterface::ID => null,
        ];
        unset($expectedCustomerData[CustomerInterface::ID]);
        $customerEntity = $this->populateCustomerEntity($customerData, [], $customerEntity);
        $customerEntity->setDefaultBilling(null);
        $customerEntity->setDefaultShipping(null);
        $customerEntity->setAddresses([]);

        $customer = $this->accountManagement->createAccount($customerEntity, '_aPassword1');
        $this->assertNotEmpty($customer->getId());
        $this->assertCustomerData($customer, $expectedCustomerData);
        $this->accountManagement->authenticate(
            $customer->getEmail(),
            '_aPassword1'
        );
    }

    /**
     * Test for create customer account for second website (with existing email for default website)
     * with global account scope config.
     *
     * @magentoConfigFixture current_store customer/account_share/scope 0
     * @magentoDataFixture Magento/Customer/_files/customer.php
     * @magentoDataFixture Magento/Store/_files/second_website_with_two_stores.php
     *
     * @return void
     */
    public function testCreateAccountInGlobalScope(): void
    {
        $customerEntity = $this->customerFactory->create();
        $this->dataObjectHelper->populateWithArray(
            $customerEntity,
            $this->defaultCustomerData,
            CustomerInterface::class
        );
        $storeId = $this->storeManager->getStore('fixture_second_store')->getStoreId();
        $customerEntity->setStoreId($storeId);
        $message = 'A customer with the same email address already exists in an associated website.';
        $this->expectExceptionObject(new InputMismatchException(__($message)));
        $this->accountManagement->createAccount($customerEntity, '_aPassword1');
    }

    /**
     * Returns random numeric string with given length.
     *
     * @param int $length
     * @return string
     */
    private static function getRandomNumericString(int $length): string
    {
        $string = '';
        for ($i = 0; $i <= $length; $i++) {
            $string .= Random::getRandomNumber(0, 9);
        }

        return $string;
    }

    /**
     * Fill in customer entity using array of customer data and additional customer data.
     *
     * @param array $customerData
     * @param array $additionalCustomerData
     * @param CustomerInterface|null $customerEntity
     * @return CustomerInterface
     */
    private function populateCustomerEntity(
        array $customerData,
        array $additionalCustomerData = [],
        ?CustomerInterface $customerEntity = null
    ): CustomerInterface {
        $customerEntity = $customerEntity ?? $this->customerFactory->create();
        $customerData = array_merge(
            $customerData,
            $additionalCustomerData
        );
        $this->dataObjectHelper->populateWithArray(
            $customerEntity,
            $customerData,
            CustomerInterface::class
        );

        return $customerEntity;
    }

    /**
     * Check that customer parameters match expected values.
     *
     * @param CustomerInterface $customer
     * @param array $expectedData
     * return void
     */
    private function assertCustomerData(
        CustomerInterface $customer,
        array $expectedData
    ): void {
        $actualCustomerArray = $customer->__toArray();
        foreach ($expectedData as $key => $expectedValue) {
            $this->assertEquals(
                $expectedValue,
                $actualCustomerArray[$key],
                "Invalid expected value for $key field."
            );
        }
    }

    /**
     * Sets config data.
     *
     * @param array $configs
     * @return void
     */
    private function setConfig(array $configs): void
    {
        foreach ($configs as $path => $value) {
            $this->mutableScopeConfig->setValue($path, $value, ScopeInterface::SCOPE_STORE, 'default');
        }
    }

    /**
     * Assert email data.
     *
     * @param array $expectedData
     * @return void
     */
    private function assertEmailData(array $expectedData): void
    {
        $message = $this->transportBuilderMock->getSentMessage();
        $this->assertNotNull($message);
        $messageFrom = $message->getFrom();
        $this->assertNotNull($messageFrom);
        $messageFrom = reset($messageFrom);
        $this->assertEquals($expectedData['name'], $messageFrom->getName());
        $this->assertEquals($expectedData['email'], $messageFrom->getEmail());
        $this->assertStringContainsString(
            $expectedData['message'],
            quoted_printable_decode($message->getBody()->bodyToString()),
            'Expected message wasn\'t found in email content.'
        );
    }

    /**
     * Returns email template id by template code.
     *
     * @param string $templateCode
     * @return int
     */
    private function getCustomTemplateId(string $templateCode): int
    {
        return (int)$this->templateCollectionFactory->create()
            ->addFieldToFilter('template_code', $templateCode)
            ->getFirstItem()
            ->getId();
    }
}
