<?php
/**
 * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @package FacebookCommerce
 */

namespace SkyVerge\WooCommerce\Facebook\Events;

use InvalidArgumentException;

defined( 'ABSPATH' ) or exit;

/**
 * Normalizer class.
 *
 */
class Normalizer {

	/**
	 * Normalizes $data according to its type
	 *
	 * @since 2.0.3
	 *
	 * @param string $field to be normalized.
	 * @param string $data value to be normalized
	 * @return string
	 * @throws InvalidArgumentException
	 */
	public static function normalize($field, $data) {
		if ($data == null || strlen($data) == 0) {
			return null;
		}

		$data = trim(strtolower($data));
		$normalized_data = $data;

		switch ($field) {
			case 'em':
			$normalized_data = Normalizer::normalizeEmail($data);
			break;

			case 'ph':
			$normalized_data = Normalizer::normalizePhone($data);
			break;

			case 'zp':
			$normalized_data = Normalizer::normalizeZipCode($data);
			break;

			case 'ct':
			$normalized_data = Normalizer::normalizeCity($data);
			break;

			case 'st':
			$normalized_data = Normalizer::normalizeState($data);
			break;

			case 'country':
			$normalized_data = Normalizer::normalizeCountry($data);
			break;

			case 'cn':
			$normalized_data = Normalizer::normalizeCountry($data);
			break;

			default:
		}

		return $normalized_data;
	}

	/**
	 * Normalizes an array containing user data
	 *
	 * @since 2.0.3
	 *
	 * @param string[] array with user data to be normalized
	 * @return string[]
	 */
	public static function normalize_array($data, $is_pixel_data){
		// Country is encoded as cn in Pixel events and country in CAPI events
		$keys_to_normalize = ['fn', 'ln', 'em', 'ph', 'zp', 'ct', 'st'];
		if($is_pixel_data){
			$keys_to_normalize[] = 'cn';
		}
		else{
			$keys_to_normalize[] = 'country';
		}
		foreach($keys_to_normalize as $key){
			if(array_key_exists($key, $data)){
				//If the data is invalid, it is erased from the array
				try{
					$data[$key] = self::normalize($key, $data[$key]);
				}
				catch(InvalidArgumentException $e){
					unset($data[$key]);
				}
			}
		}
		return $data;
	}

	/**
	 * Normalizes an email
	 *
	 * @since 2.0.3
	 *
	 * @param string $email Email address to be normalized.
	 * @return string
	 * @throws InvalidArgumentException
	 */
	private static function normalizeEmail($email) {
		// Validates email against RFC 822
		$result = filter_var($email, FILTER_SANITIZE_EMAIL);

		if (!filter_var($result, FILTER_VALIDATE_EMAIL)) {
			throw new InvalidArgumentException('Invalid email format for the passed email: ' . $email . 'Please check the passed email format.');
		}

		return $result;
	}

	/**
	 * Normalizes a city name
	 *
	 * @since 2.0.3
	 *
	 * @param string $city city name to be normalized.
	 * @return string
	 */
	private static function normalizeCity($city) {
		return trim(preg_replace('/[0-9.\s\-()]/', '', $city));
	}

	/**
	 * Normalizes a state code
	 *
	 * @since 2.0.3
	 *
	 * @param string $state state name to be normalized.
	 * @return string
	 */
	private static function normalizeState($state) {
		return preg_replace('/[^a-z]/', '', $state);
	}

	/**
	 * Normalizes a country code
	 *
	 * @since 2.0.3
	 *
	 * @param string $country country code to be normalized(ISO 3166-2).
	 * @return string
	 * @throws InvalidArgumentException
	 */
	private static function normalizeCountry($country) {
		$result = preg_replace('/[^a-z]/i', '', $country);

		if (strlen($result) != 2) {
			throw new InvalidArgumentException('Invalid country format passed(' . $country . '). Country Code should be a two-letter ISO Country Code');
		}

		return $result;
	}

	/**
	 * Normalizes a zip code
	 *
	 * @since 2.0.3
	 *
	 * @param string $zip postal code to be normalized.
	 * @return string
	 */
	private static function normalizeZipCode($zip) {
		// Removing the spaces from the zip code. Eg:
		$zip = preg_replace('/[ ]/', '', $zip);

		// If the code has more than one part, retain the first part.
		$zip = explode('-', $zip)[0];
		return $zip;
	}

	/**
	 * Normalizes a phone number
	 *
	 * @since 2.0.3
	 *
	 * @param string $phone phone number to be normalized.
	 * @return string
	 */
	private static function normalizePhone($phone) {
		$result = trim(preg_replace('/[a-z()-]/', '', $phone));

		if (Normalizer::isInternationalNumber($result)) {
			$result = preg_replace('/[\-\s+]/', '', $result);
		}

		return $result;
	}

	/**
	 * Checks if a phone number is international
	 *
	 * @since 2.0.3
	 *
	 * @param string $phone_number Phone number to be normalized.
	 * @return bool
	 */
	private static function isInternationalNumber($phone_number) {
		// Remove spaces and hyphens
		$phone_number = preg_replace('/[\-\s]/', '', $phone_number);

		// Strip + and up to 2 leading 0s
		$phone_number = preg_replace('/^\+?0{0,2}/', '', $phone_number);

		if (substr($phone_number, 0, 1) === '0') {
			return false;
		}

		// International Phone number with country calling code.
		$international_number_regex = '/^\d{1,4}\(?\d{2,3}\)?\d{4,}$/';

		return preg_match($international_number_regex, $phone_number);
	}
}
