<?php

namespace Mtc\Core;

use Mtc\Core\Models\Country;

/**
 * Postcode class.
 * What it can do:
 * - Normalize postcodes - d02af30 to D02 AF30 for Ireland
 * - Validate the postcode against the RegEx
 *
 * @author Jack Donaldson - jack.donaldson@mtcmedia.co.uk
 * @version 23.02.2017
 *
 * This offers validation for postcodes.
 * It is done by taking the Countries' two character code and uses it to get the regex for that specific country
 *
 * - Sources -
 *
 *  Postcodes in Ireland
 *  https://en.wikipedia.org/wiki/Postal_addresses_in_the_Republic_of_Ireland
 *
 *  Postcodes around the world
 *  https://en.wikipedia.org/wiki/List_of_postal_codes
 */
class GenericPostcode implements Postcode
{
    public $regex;

    public $postcode;

     /**
     * Postcode constructor.
     *
     * @param string $postcode_string Postcode as string Expected Input: "12345", "1234", "LT-12345", "12345-1234", "DD1 3JA"
     *
     * @param string $country Country Code as string Expected Input: 'IE', 'DE' or 'US'
     */
    public function __construct($postcode_string, $country)
    {
        // Resolve to a 2-letter country code if a name was passed
        $countryCode = \strlen($country) === 2
            ? \strtoupper($country)
            : (Country::query()->where('name', $country)->value('code') ?? \strtoupper($country));

        // Country-specific normalisation
        if ($countryCode === 'IE') {
            $postcode_string = self::normalize($postcode_string);
        } else {
            $postcode_string = \strtoupper((string) $postcode_string);
        }

        $regex = Country::getRegex($countryCode);

        // If no regex is configured for the country, treat as “no postcode constraint”
        // (docblock says: assume country doesn’t require a postcode).
        $this->regex = $regex ? ('/^' . $regex . '$/') : '/^.*$/';

        $this->postcode = $postcode_string;
    }


    /**
     * Normalizes postcode format to uppercase with space.
     * Brings d02af30 to D02 AF30 for Ireland
     * $space_position means is used in the regex to show where a space is supposed to be.
     * If the country does not require spaces, use strtoupper() instead.
     *
     * THIS IS CURRENTLY ONLY GETTING USED BY IRELAND
     *
     * @param string $postcode_string Expected Input: dd13ja, d02af30
     *
     * @return string
     */
    public function normalize($postcode_string)
    {
        $postcode_string = strtoupper($postcode_string);

        // adjust postcode with no spaces
        if (strpos($postcode_string, ' ') === false) {
            $postcode_string = preg_replace('#(.{4})$#', ' $1', $postcode_string);
        }

        return $postcode_string;
    }

    /*
     * Checks if a string validates as a postcode in the selected country.
     *
     * Countries are in the form of their two character code; "GB", "LT", etc
     *
     * Most Countries postalcodes are a series of numbers and sometimes letters, so expected inputs for $postcode_string are "12345",
     * "1234", "LT-12345", "12345-1234", etc
     *
     * @param string $postcode_string Expected Input: "12345", "1234", "LT-12345", "12345-1234", "DD1 3JA"
     * @param string $country Expected input: "DR", "FR", "US", etc
     * @return boolean
     */
    public function validates()
    {
        return preg_match($this->regex, $this->__toString());
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return $this->postcode;
    }


}
