<?php

namespace CommerceGuys\Addressing\Tests\AddressFormat;

use CommerceGuys\Addressing\AddressFormat\AddressField;
use CommerceGuys\Addressing\AddressFormat\AddressFormat;
use CommerceGuys\Addressing\AddressFormat\AdministrativeAreaType;
use CommerceGuys\Addressing\AddressFormat\DependentLocalityType;
use CommerceGuys\Addressing\AddressFormat\LocalityType;
use CommerceGuys\Addressing\AddressFormat\PostalCodeType;
use PHPUnit\Framework\TestCase;

/**
 * @coversDefaultClass \CommerceGuys\Addressing\AddressFormat\AddressFormat
 */
final class AddressFormatTest extends TestCase
{
    /**
     * @covers ::__construct
     */
    public function testMissingFormat(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('Missing required property format.');
        $definition = [
            'country_code' => 'US',
        ];
        $addressFormat = new AddressFormat($definition);
    }

    /**
     * @covers ::__construct
     */
    public function testInvalidDefaultValue(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('"unknown" is not a valid AddressField value.');
        $definition = [
            'country_code' => 'US',
            'format' => "%givenName %familyName\n%organization\n%addressLine1\n%addressLine2\n%addressLine3\n%dependentLocality",
            'default_values' => ['unknown' => 'CA'],
        ];
        $addressFormat = new AddressFormat($definition);
    }

    /**
     * @covers ::__construct
     */
    public function testInvalidDependentLocalityType(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('"WRONG" is not a valid DependentLocalityType value.');
        $definition = [
            'country_code' => 'US',
            'format' => "%givenName %familyName\n%organization\n%addressLine1\n%addressLine2\n%addressLine3\n%dependentLocality",
            'required_fields' => [AddressField::ADDRESS_LINE1],
            'dependent_locality_type' => 'WRONG',
        ];
        $addressFormat = new AddressFormat($definition);
    }

    /**
     * @covers ::__construct
     * @covers ::getCountryCode
     * @covers ::getLocale
     * @covers ::getFormat
     * @covers ::getLocalFormat
     * @covers ::getUsedFields
     * @covers ::getUsedSubdivisionFields
     * @covers ::getRequiredFields
     * @covers ::getUppercaseFields
     * @covers ::getDefaultValues
     * @covers ::getAdministrativeAreaType
     * @covers ::getLocalityType
     * @covers ::getDependentLocalityType
     * @covers ::getPostalCodeType
     * @covers ::getPostalCodePattern
     * @covers ::getPostalCodePrefix
     * @covers ::getSubdivisionDepth
     */
    public function testValid(): void
    {
        $definition = [
            'country_code' => 'US',
            'locale' => 'en',
            'format' => "%givenName %familyName\n%organization\n%addressLine1\n%addressLine2\n%addressLine3\n%locality, %administrativeArea %postalCode",
            // The local format is made up, US doesn't have one usually.
            'local_format' => '%postalCode\n%addressLine1\n%organization\n%givenName %familyName',
            'required_fields' => [
                AddressField::ADMINISTRATIVE_AREA,
                AddressField::LOCALITY,
                AddressField::POSTAL_CODE,
                AddressField::ADDRESS_LINE1,
            ],
            'uppercase_fields' => [
                AddressField::ADMINISTRATIVE_AREA,
                AddressField::LOCALITY,
            ],
            'default_values' => [
                AddressField::ADMINISTRATIVE_AREA => 'CA',
            ],
            'administrative_area_type' => AdministrativeAreaType::STATE,
            'locality_type' => LocalityType::CITY,
            'dependent_locality_type' => DependentLocalityType::DISTRICT,
            'postal_code_type' => PostalCodeType::ZIP,
            'postal_code_pattern' => '(\d{5})(?:[ \-](\d{4}))?',
            // US doesn't use postal code prefixes, fake one for test purposes.
            'postal_code_prefix' => 'US',
            'subdivision_depth' => 1,
        ];
        $addressFormat = new AddressFormat($definition);

        $this->assertEquals($definition['country_code'], $addressFormat->getCountryCode());
        $this->assertEquals($definition['locale'], $addressFormat->getLocale());
        $this->assertEquals($definition['format'], $addressFormat->getFormat());
        $this->assertEquals($definition['local_format'], $addressFormat->getLocalFormat());
        $this->assertEquals($definition['required_fields'], $addressFormat->getRequiredFields());
        $this->assertEquals($definition['uppercase_fields'], $addressFormat->getUppercaseFields());
        $this->assertEquals($definition['default_values'], $addressFormat->getDefaultValues());
        $this->assertEquals($definition['administrative_area_type'], $addressFormat->getAdministrativeAreaType());
        $this->assertEquals($definition['locality_type'], $addressFormat->getLocalityType());
        // The format has no %dependentLocality, the type must be NULL.
        $this->assertNull($addressFormat->getDependentLocalityType());
        $this->assertEquals($definition['postal_code_type'], $addressFormat->getPostalCodeType());
        $this->assertEquals($definition['postal_code_pattern'], $addressFormat->getPostalCodePattern());
        $this->assertEquals($definition['postal_code_prefix'], $addressFormat->getPostalCodePrefix());
        $this->assertEquals($definition['subdivision_depth'], $addressFormat->getSubdivisionDepth());

        $expectedUsedFields = [
            AddressField::ADMINISTRATIVE_AREA,
            AddressField::LOCALITY,
            AddressField::POSTAL_CODE,
            AddressField::ADDRESS_LINE1,
            AddressField::ADDRESS_LINE2,
            AddressField::ADDRESS_LINE3,
            AddressField::ORGANIZATION,
            AddressField::GIVEN_NAME,
            AddressField::FAMILY_NAME,
        ];
        $this->assertEquals($expectedUsedFields, $addressFormat->getUsedFields());
        $expectedUsedSubdivisionFields = [
            AddressField::ADMINISTRATIVE_AREA,
            AddressField::LOCALITY,
        ];
        $this->assertEquals($expectedUsedSubdivisionFields, $addressFormat->getUsedSubdivisionFields());
    }
}
