<?php

namespace Planaday\Utility;

use Closure;

class CssClassAndIdConverter {
	protected const SAFE_REGEX_REPLACE_PREFIX = "(([},]\s*?)([^\s,}.#>'\"]*))\\";
	protected const UNSAFE_REGEX_REPLACE_PREFIX = "((([},\]]|(/span>))\s*?)((([^,}\s'\"]+\s+)*?)([^\s,}.#'\"]*)))\\";
	protected const DANGEROUS_REGEX_REPLACE_PREFIX = "(((([},\]]|(/span>))\s*?)((([^,}\s'\"]+\s+)*?)(([^\s,}.#'\">]*\s*[.#>])*([^\s,}.#'\"]*))))|(/span>))\\";

	protected const CONVERSION_ARRAY_SINGLE_LAYER = [
		'#errors'         => '.pad-errors',
		'#pad-errors'     => '.pad-errors',
		'#pad-formerrors' => '.pad-form-errors',
		'#pad-ok'         => '.pad-ok',

		'#cursustitel'        => '.pad-cursus-title',
		'#padbooking'         => '.pad-booking-form',
		'#materials'          => '.pad-materials',
		'#pad-company-choice' => '.pad-company-choice',
		'#company_choice'     => '.pad-company-choice-radio',
		'.radio'              => '.pad-radio',
		'.padform'            => '.pad-form',

		'#pad-company'     => '.pad-company',
		'#company_details' => '.pad-company.pad-toggle-company',

		'#pad-company_name' => '.pad-company-name',
		'#company_name'     => '.pad-company.pad-input-name',
		'.company_name'     => '.pad-company.pad-input-name',

		'.pad-address-info' => '.pad-postal-code',

		'#pad-street'                     => '.pad-company.pad-address',
		'#company_address'                => '.pad-company-address.pad-input-street',
		'#company_house_number'           => '.pad-company-address.pad-input-house_number',
		'.company_house_number'           => '.pad-company-address.pad-input-house_number',
		'.company_address'                => '.pad-company-address.pad-input-address',
		'#company_house_number_extension' => '.pad-company-address.pad-input-house_number_extension',
		'.company_house_number_extension' => '.pad-company-address.pad-input-house_number_extension',
		'#pad-company_postal_code'        => '.pad-company.pad-postal-code-label',
		'#company_postal_code'            => '.pad-company-address.pad-input-postal_code',
		'.company_postal_code'            => '.pad-company-address.pad-input-postal_code',
		'#company_city'                   => '.pad-company-address.pad-input-city',
		'.company_city'                   => '.pad-company-address.pad-input-city',

		'#pad-emailadres-bedrijf' => '.pad-company-email',
		'#company_email'          => '.pad-company.pad-input-email',
		'.company_email'          => '.pad-company.pad-input-email',
		'#pad-telefoon-bedrijf'   => '.pad-company.pad-phone-number',
		'#company_phonenumber'    => '.pad-company.pad-input-phone_number',
		'.company_phonenumber'    => '.pad-company.pad-input-phone_number',

		'#pad-invoice'     => '.pad-invoice.pad-invoice-title',
		'#pad-invoicemail' => '.pad-invoice.pad-email',
		'#invoice_email'   => '.pad-invoice.pad-input-email',
		'.invoice_email'   => '.pad-invoice.pad-input-email',

		'#pad-invoice-street'              => '.pad-invoice.pad-address',
		'#pad-invoice-company_postal_code' => '.pad-postal-code.pad-invoice',
		'#invoice_postal_code'             => '.pad-invoice.pad-input-postal_code',
		'.invoice_postal_code'             => '.pad-invoice.pad-input-postal_code',
		'#invoice_city'                    => '.pad-invoice.pad-input-city',
		'.invoice_city'                    => '.pad-invoice.pad-input-city',
		'#invoice_country'                 => '.pad-invoice.pad-input-country',
		'.invoice_country'                 => '.pad-invoice.pad-input-country',
		'#invoice_addressee'               => '.pad-invoice.pad-input-name',
		'.invoice_addressee'               => '.pad-invoice.pad-input-name',
		'#invoice_address'                 => '.pad-invoice.pad-input-street',
		'.invoice_address'                 => '.pad-invoice.pad-input-street',
		'#invoice_house_number'            => '.pad-invoice.pad-input-house_number',
		'.invoice_house_number'            => '.pad-invoice.pad-input-house_number',
		'#invoice_house_number_extension'  => '.pad-invoice.pad-input-house_number_extension',
		'.invoice_house_number_extension'  => '.pad-invoice.pad-input-house_number_extension',

		'#pad-api-attributes' => '.pad-api-attributes',

		'#extraPersonTemplate' => '.pad-template-student-form',
		'.extraPersonTemplate' => '.pad-template-student-form',
		'.extraPerson'         => '.pad-student',
		'.removeRow'           => '.pad-remove-student',

		'#pad-name-block' => '.pad-name-block',
		'.pad-naam'       => '.pad-name',
		'#gender'         => '.pad-student.pad-input-gender',
		'.pad-geslacht'   => '.pad-student.pad-input-gender',
		'#initials'       => '.pad-student.pad-input-initials',
		'.initials'       => '.pad-student.pad-input-initials',
		'#first_name'     => '.pad-student.pad-input-first_name',
		'.first_name'     => '.pad-student.pad-input-first_name',
		'#nick_name'      => '.pad-student.pad-input-nick_name',
		'.nick_name'      => '.pad-student.pad-input-nick_name',
		'#prefix'         => '.pad-student.pad-input-prefix',
		'.prefix'         => '.pad-student.pad-input-prefix',
		'#last_name'      => '.pad-student.pad-input-last_name',
		'.last_name'      => '.pad-student.pad-input-last_name',
		'#maiden_name'    => '.pad-student.pad-input-maiden_name',
		'.maiden_name'    => '.pad-student.pad-input-maiden_name',

		'.pad-cursist-straat'     => '.pad-street-block.pad-student-address',
		'#address'                => '.pad-student-address.pad-input-street',
		'.address'                => '.pad-student-address.pad-input-street',
		'#house_number'           => '.pad-student-address.pad-input-house_number',
		'.house_number'           => '.pad-student-address.pad-input-house_number',
		'#house_number_extension' => '.pad-student-address.pad-input-house_number_extension',
		'.house_number_extension' => '.pad-student-address.pad-input-house_number_extension',
		'.pad-cursist-postcode'   => '.pad-postal-code.pad-student-address',
		'#postal_code'            => '.pad-student-address.pad-input-postal_code',
		'.postal_code'            => '.pad-student-address.pad-input-postal_code',
		'#city'                   => '.pad-student-address.pad-input-city',
		'.city'                   => '.pad-student-address.pad-input-city',

		'.pad-emailadres-cursist' => '.pad-student-email-address',
		'#email'                  => '.pad-student.pad-input-email',
		'.email'                  => '.pad-student.pad-input-email',

		'.pad-telefoonnummer-cursist' => '.pad-student-phone-number',
		'#phone_number'               => '.pad-student.pad-input-phone_number',
		'.phone_number'               => '.pad-student.pad-input-phone_number',

		'.pad-geboortedatum-cursist' => '.pad-student-date-of-birth',
		'#date_of_birth'             => '.pad-student.pad-input-date_of_birth',
		'.date_of_birth'             => '.pad-student.pad-input-date_of_birth',

		'.pad-geboorteplaats-cursist' => '.pad-student-place-of-birth',
		'#hometown'                   => '.pad-student.pad-input-place_of_birth',
		'.hometown'                   => '.pad-student.pad-input-place_of_birth',

		'.pad-functie-cursist' => '.pad-student-company-position',
		'#company_position'    => '.pad-student.pad-input-company_position',
		'.company_position'    => '.pad-student.pad-input-company_position',

		'.pad-kostenplaats-cursist' => '.pad-student-cost-center-code',
		'#costcentercode'           => '.pad-student.pad-input-cost_center_code',
		'.costcentercode'           => '.pad-student.pad-input-cost_center_code',

		'.pad-personeelsnummer-cursist' => '.pad-student-employee-number',
		'#employeeId'                   => '.pad-student.pad-input-employee_number',
		'.employeeId'                   => '.pad-student.pad-input-employee_number',

		'.pad-internal_reference-cursist' => '.pad-student-internal-reference',
		'#internal_reference'             => '.pad-student.pad-input-internal_reference',
		'.internal_reference'             => '.pad-student.pad-input-internal_reference',

		'.pad-opmerking-cursist' => '.pad-student-remark',
		'#remark'                => '.pad-student.pad-input-remark',
		'.remark'                => '.pad-student.pad-input-remark',

		'.contactdiv' => '.pad-student-contact',
		'#iscontactperson'        => '.pad-student.pad-input-is_contact',
		'.iscontactperson'        => '.pad-student.pad-input-is_contact',

		'.stapquestion' => '.pad-student-stap',
		'.stap_regulation'        => '.pad-student.pad-input-stap_regulation',

		'.code95question' => '.pad-student-code95',
		'#process_code95'         => '.pad-student.pad-input-process_code95',
		'.process_code95'         => '.pad-student.pad-input-process_code95',

		'.soobquestion' => '.pad-student-soob',
		'#process_soob'           => '.pad-student.pad-input-process_soob',
		'.process_soob'           => '.pad-student.pad-input-process_soob',

		'#pad-freefields'            => '.pad-extra-fields',
		'.pad-freefield-block'       => '.pad-extra-field-block',
		'.pad-freefield-title'       => '.pad-extra-field-title',
		'.pad-freefield-description' => '.pad-extra-field-description',
		'.pad-freefrield-fieldset'   => '.pad-extra-field-fieldset',

		'.formerrorlabel' => '.pad-form-error-label',
		'.formerror'      => '.pad-form-error',

		'#container' => '.pad-extra-student-output',

		'#button_extra_student' => '.pad-add-student',
		'#addRow'               => '.pad-add-student-button',

		'#pad-verplicht' => '.pad-required-message',

		'#algemenevoorwaarden' => '.pad-terms-and-conditions',

		'#pad-submitbutton' => '.pad-submit-button',
		'.pad-submitbutton' => '.pad-submit-button'
	];
	protected const CONVERSION_ARRAY_MULTILAYER = [
		'#companychoiceyes' => '.pad-company-choice-radio > .pad-radio-input-1',
		'#companychoiceno'  => '.pad-company-choice-radio > .pad-radio-input-0',

		'.pad-email-cursist'      => '.pad-student-email-address > label',
		'.pad-telefoon-cursist'   => '.pad-student-phone-number > label',
		'.pad-geboortedatum'      => '.pad-student-date-of-birth > label',
		'.pad-geboorteplaats'     => '.pad-student-place-of-birth > label',
		'.pad-functie'            => '.pad-student-company-position > label',
		'.pad-kostenplaats'       => '.pad-student-cost-center-code > label',
		'.pad-personeelsnummer'   => '.pad-student-employee-number > label',
		'.pad-internal_reference' => '.pad-student-internal-reference > label',
		'.pad-opmerking'          => '.pad-student-remark > label',
		'#stap_regulation'        => '.pad-student-stap > label',

		'#algemenevoorwaarden-check' => '.pad-terms-and-conditions > label > input',
	];

	/**
	 * @param string $css
	 * @param Closure(string $oldClass, string $newClass):string $successWrappingClosure
	 * @param Closure(string $oldClass, string $newClass):string $riskWrappingClosure
	 * @param Closure(string $oldClass, string $newClass):string $dangerWrappingClosure
	 *
	 * @return string
	 */
	public function convertCss( string $css, Closure $successWrappingClosure, Closure $riskWrappingClosure, Closure $dangerWrappingClosure ): string {
		$css = '} ' . $css;
		$css = $this->convertSafe( $css, $successWrappingClosure );
		$css = $this->convertUnsafe( $css, $riskWrappingClosure );
		$css = $this->convertDangerous( $css, $dangerWrappingClosure );

		return substr( $css, 2 );
	}

	/**
	 * @param string $css
	 * @param Closure(string $oldClass, string $newClass):string $successWrappingClosure
	 *
	 * @return string
	 */
	private function convertSafe( string $css, Closure $successWrappingClosure ): string {
		$conversions = self::CONVERSION_ARRAY_SINGLE_LAYER;
		uksort( $conversions, function ( string $a, string $b ) {
			return strlen( $b ) - strlen( $a );
		} );

		foreach ( $conversions as $old => $new ) {
			if($css !== null) $css = preg_replace( "@" . self::SAFE_REGEX_REPLACE_PREFIX . $old . "@", '${2}' . '${3}' . $successWrappingClosure( $old, $new ), $css );

			if($css !== null) $css = preg_replace( "@" . self::UNSAFE_REGEX_REPLACE_PREFIX . $old . "@", '${1}' . $successWrappingClosure( $old, $new ), $css );

			if($css !== null) $css = preg_replace( "@" . self::DANGEROUS_REGEX_REPLACE_PREFIX . $old . "@", '${1}' . $successWrappingClosure( $old, $new ), $css );
		}

		$conversions = self::CONVERSION_ARRAY_MULTILAYER;
		uksort( $conversions, function ( string $a, string $b ) {
			return strlen( $a ) - strlen( $b );
		} );

		foreach ( $conversions as $old => $new ) {
			if($css !== null) $css = preg_replace( "@" . self::SAFE_REGEX_REPLACE_PREFIX . $old . "@", '${2}' . $successWrappingClosure( '${3}' . $old, $new ), $css );
		}

		return $css ?? '';
	}

	/**
	 * @param string $css
	 * @param Closure(string $oldClass, string $newClass):string $unsafeWrappingClosure
	 *
	 * @return string
	 */
	private function convertUnsafe( string $css, Closure $unsafeWrappingClosure ): string {
		$conversions = self::CONVERSION_ARRAY_MULTILAYER;
		uksort( $conversions, function ( string $a, string $b ) {
			return strlen( $a ) - strlen( $b );
		} );

		foreach ( $conversions as $old => $new ) {
			if($css !== null) $css = preg_replace( "@" . self::UNSAFE_REGEX_REPLACE_PREFIX . $old . "@", '${1}' . $unsafeWrappingClosure( $old, $new ), $css );
		}

		return $css ?? '';
	}

	/**
	 * @param string $css
	 * @param Closure(string $oldClass, string $newClass):string $dangerousWrappingClosure
	 *
	 * @return string
	 */
	private function convertDangerous( string $css, Closure $dangerousWrappingClosure ): string {
		$conversions = self::CONVERSION_ARRAY_MULTILAYER;
		uksort( $conversions, function ( string $a, string $b ) {
			return strlen( $a ) - strlen( $b );
		} );

		foreach ( $conversions as $old => $new ) {
			if($css !== null) $css = preg_replace( "@" . self::DANGEROUS_REGEX_REPLACE_PREFIX . $old . "@", '${1}' . $dangerousWrappingClosure( $old, $new ), $css );
		}

		return $css ?? '';
	}
}