<?php

namespace GriffinForms\Frontend\Html\Forms\Fields;

final class InputPhone extends \GriffinForms\Frontend\Html\Forms\Fields\Format
{
    protected $selected_dial_code = '';
    protected $local_phone_value = '';

    protected function formControl($iteration = 0)
    {
        $iteration = absint($iteration);
        $select_id = 'griffinforms-phone-country-' . absint($this->id) . '-' . $iteration;
        $field_settings = $this->item->getProp('field_settings');
        if (is_string($field_settings)) {
            $decoded = maybe_unserialize($field_settings);
            if (is_array($decoded)) {
                $field_settings = $decoded;
            }
        }
        $display_mode = is_array($field_settings) && !empty($field_settings['phone_display_mode'])
            ? $field_settings['phone_display_mode']
            : 'flag_code';

        $allowed_codes = isset($this->validations['allowed_country_codes']) && is_array($this->validations['allowed_country_codes'])
            ? $this->validations['allowed_country_codes']
            : array();
        $countries = \GriffinForms\Includes\Helpers\PhoneCountries::getCountries($allowed_codes);

        $default_code = $this->selected_dial_code;
        if ($default_code === '' && !empty($allowed_codes) && count($allowed_codes) === 1) {
            $default_code = '+' . ltrim((string) $allowed_codes[0], '+');
        }

        $select_disabled = $this->isDisabled() ? ' disabled="disabled"' : '';

        $html  = '<div class="input-group griffinforms-phone-input-group mb-1">';
        $html .= '<select class="form-select griffinforms-phone-country-select" id="' . esc_attr($select_id) . '" data-gf-phone-display-mode="' . esc_attr($display_mode) . '"' . $select_disabled . '>';
        $html .= '<option value="">' . esc_html__('Country', 'griffinforms-form-builder') . '</option>';
        foreach ($countries as $country) {
            $selected = ($default_code !== '' && $country['dial_code'] === $default_code) ? ' selected' : '';
            $label = $country['dial_code'];
            if ($display_mode === 'flag_code') {
                $label = $country['flag'] . ' ' . $country['dial_code'];
            } elseif ($display_mode === 'flag_code_name') {
                $label = $country['flag'] . ' ' . $country['dial_code'] . ' ' . $country['name'];
            }
            $html .= '<option value="' . esc_attr($country['dial_code']) . '" data-iso="' . esc_attr($country['iso']) . '" data-flag="' . esc_attr($country['flag']) . '" data-name="' . esc_attr($country['name']) . '"' . $selected . '>';
            $html .= esc_html($label);
            $html .= '</option>';
        }
        $html .= '</select>';
        $html .= '<input type="tel"';
        $html .= ' class="' . esc_attr($this->html_ids['formcontrol']) . ' form-control"';
        $html .= ' inputmode="tel"';
        $html .= ' aria-label="' . esc_attr__('Telephone', 'griffinforms-form-builder') . '"';
        $html .= ' data-gf-phone-country-select="' . esc_attr($select_id) . '"';
        $html .= $this->requiredAttr();
        $html .= $this->placeholderAttr();
        $html .= ' value="' . esc_attr($this->local_phone_value) . '"';
        $html .= $this->readOnlyAttr();
        $html .= $this->disabledAttr();
        $html .= $this->autocompleteAttr();
        $html .= ' data-gf-formpage="' . absint($this->data['formpage']) . '">';
        $html .= '</div>';

        return $html;
    }

    protected function setErrors()
    {
        $this->errors = array(
            'required',
            'phone',
            'mindigits',
            'maxdigits',
            'formattype',
            'extension',
            'allowedcountrycodes',
            'pattern',
            'unique',
            'dbValidate'
        );
    }

    protected function isPhone()
    {
        return true;
    }

    protected function isMindigits()
    {
        return $this->isCustomValidations()
            && isset($this->validations['min_digits'])
            && is_numeric($this->validations['min_digits'])
            && (int) $this->validations['min_digits'] > 0;
    }

    protected function isMaxdigits()
    {
        return $this->isCustomValidations()
            && isset($this->validations['max_digits'])
            && is_numeric($this->validations['max_digits'])
            && (int) $this->validations['max_digits'] > 0;
    }

    protected function isFormattype()
    {
        return $this->isCustomValidations()
            && !empty($this->validations['format_type'])
            && $this->validations['format_type'] !== 'any_format';
    }

    protected function isExtension()
    {
        return $this->isCustomValidations()
            && array_key_exists('allow_extension', $this->validations)
            && (int) $this->validations['allow_extension'] === 0;
    }

    protected function isAllowedcountrycodes()
    {
        return $this->isCustomValidations()
            && !empty($this->validations['allowed_country_codes'])
            && is_array($this->validations['allowed_country_codes']);
    }

    protected function phoneJs($error)
    {
        echo '
            $(actionBtn).hover(function(){
                $(formControl).each(checkPhoneFormat);
            });
            $(formControl).on("input change", checkPhoneFormat);

            function checkPhoneFormat() {
                let value = $.trim($(this).val());
                const selectId = $(this).data("gf-phone-country-select");
                const selectVal = selectId ? $("#" + selectId).val() : "";
                const combined = buildPhoneCombinedValue(value, selectVal);

                if (value === "") {
                    removeAlert($(this), "' . esc_js($error) . '");
                    return;
                }

                const basicPattern = /^[0-9()+\\-\\s.#xextEXT]*$/;
                const digits = combined.replace(/\\D/g, "");

                if (!basicPattern.test(value) || digits.length < 5) {
                    addAlert($(this), "' . esc_js($error) . '");
                } else {
                    removeAlert($(this), "' . esc_js($error) . '");
                }
            }
        ';
    }

    protected function mindigitsJs($error)
    {
        $min = isset($this->validations['min_digits']) ? absint($this->validations['min_digits']) : 0;
        if ($min <= 0) {
            return;
        }

        echo '
            $(actionBtn).hover(function(){
                $(formControl).each(checkMinDigits);
            });
            $(formControl).on("input change", checkMinDigits);

            function checkMinDigits() {
                let value = $.trim($(this).val());
                const selectId = $(this).data("gf-phone-country-select");
                const selectVal = selectId ? $("#" + selectId).val() : "";
                const combined = buildPhoneCombinedValue(value, selectVal);

                if (value === "") {
                    removeAlert($(this), "' . esc_js($error) . '");
                    return;
                }

                const digits = combined.replace(/\\D/g, "");
                if (digits.length > 0 && digits.length < ' . $min . ') {
                    addAlert($(this), "' . esc_js($error) . '");
                } else {
                    removeAlert($(this), "' . esc_js($error) . '");
                }
            }
        ';
    }

    protected function maxdigitsJs($error)
    {
        $max = isset($this->validations['max_digits']) ? absint($this->validations['max_digits']) : 0;
        if ($max <= 0) {
            return;
        }

        echo '
            $(actionBtn).hover(function(){
                $(formControl).each(checkMaxDigits);
            });
            $(formControl).on("input change", checkMaxDigits);

            function checkMaxDigits() {
                let value = $.trim($(this).val());
                const selectId = $(this).data("gf-phone-country-select");
                const selectVal = selectId ? $("#" + selectId).val() : "";
                const combined = buildPhoneCombinedValue(value, selectVal);

                if (value === "") {
                    removeAlert($(this), "' . esc_js($error) . '");
                    return;
                }

                const digits = combined.replace(/\\D/g, "");
                if (digits.length > ' . $max . ') {
                    addAlert($(this), "' . esc_js($error) . '");
                } else {
                    removeAlert($(this), "' . esc_js($error) . '");
                }
            }
        ';
    }

    protected function formattypeJs($error)
    {
        $format_type = isset($this->validations['format_type']) ? $this->validations['format_type'] : '';
        if ('' === $format_type || 'any_format' === $format_type) {
            return;
        }

        echo '
            $(actionBtn).hover(function(){
                $(formControl).each(checkFormatType);
            });
            $(formControl).on("input change", checkFormatType);

            function checkFormatType() {
                let value = $.trim($(this).val());
                const selectId = $(this).data("gf-phone-country-select");
                const selectVal = selectId ? $("#" + selectId).val() : "";
                const combined = buildPhoneCombinedValue(value, selectVal);

                if (value === "") {
                    removeAlert($(this), "' . esc_js($error) . '");
                    return;
                }

                const digits = combined.replace(/\\D/g, "");

                if ("' . esc_js($format_type) . '" === "international_format") {
                    if (!value.startsWith("+") && !value.startsWith("00") && !selectVal) {
                        addAlert($(this), "' . esc_js($error) . '");
                        return;
                    }
                    if (digits.length < 8 || digits.length > 15) {
                        addAlert($(this), "' . esc_js($error) . '");
                        return;
                    }
                } else if ("' . esc_js($format_type) . '" === "national_format") {
                    if (value.startsWith("+") && !selectVal) {
                        addAlert($(this), "' . esc_js($error) . '");
                        return;
                    }
                    if (digits.length > 0 && digits.length < 6) {
                        addAlert($(this), "' . esc_js($error) . '");
                        return;
                    }
                }

                removeAlert($(this), "' . esc_js($error) . '");
            }
        ';
    }

    protected function extensionJs($error)
    {
        echo '
            $(actionBtn).hover(function(){
                $(formControl).each(checkExtension);
            });
            $(formControl).on("input change", checkExtension);

            function checkExtension() {
                let value = $.trim($(this).val());

                if (value === "") {
                    removeAlert($(this), "' . esc_js($error) . '");
                    return;
                }

                const extensionPattern = /(ext\\.?|extension|x|#)\\s*\\d+$/i;
                if (extensionPattern.test(value)) {
                    addAlert($(this), "' . esc_js($error) . '");
                } else {
                    removeAlert($(this), "' . esc_js($error) . '");
                }
            }
        ';
    }

    protected function allowedcountrycodesJs($error)
    {
        $codes = isset($this->validations['allowed_country_codes']) && is_array($this->validations['allowed_country_codes'])
            ? array_values(array_filter($this->validations['allowed_country_codes'], 'strlen'))
            : [];

        if (empty($codes)) {
            return;
        }

        $encoded_codes = wp_json_encode(array_values($codes));
        $var_name = sanitize_key('allowedCodesField' . absint($this->id));

        echo '
            const ' . $var_name . ' = ' . $encoded_codes . ';

            $(actionBtn).hover(function(){
                $(formControl).each(checkAllowedCountryCodes);
            });
            $(formControl).on("input change", checkAllowedCountryCodes);

            function checkAllowedCountryCodes() {
                let value = $.trim($(this).val());
                const selectId = $(this).data("gf-phone-country-select");
                const selectVal = selectId ? $("#" + selectId).val() : "";

                if (value === "") {
                    removeAlert($(this), "' . esc_js($error) . '");
                    return;
                }

                const digits = selectVal ? String(selectVal).replace(/\\D/g, "") : value.replace(/\\D/g, "");
                if (!digits.length) {
                    removeAlert($(this), "' . esc_js($error) . '");
                    return;
                }

                const matches = ' . $var_name . '.some(function(code){
                    return digits.startsWith(String(code));
                });

                if (matches) {
                    removeAlert($(this), "' . esc_js($error) . '");
                } else {
                    addAlert($(this), "' . esc_js($error) . '");
                }
            }
        ';
    }

    protected function subItemJs()
    {
        $id = absint($this->id);

        echo '
            function buildPhoneCombinedValue(value, dialCode) {
                value = String(value || "").trim();
                if (!value) {
                    return "";
                }
                if (value.startsWith("+")) {
                    return "+" + value.replace(/\\D/g, "");
                }
                if (value.startsWith("00")) {
                    return "+" + value.replace(/\\D/g, "").replace(/^00/, "");
                }
                if (dialCode) {
                    const digits = value.replace(/\\D/g, "");
                    return dialCode + digits;
                }
                return value;
            }

            // No prefix UI to update; dropdown label handles display.
        ';
    }

    protected function syncRenderedValue(int $iteration = 0): void
    {
        parent::syncRenderedValue($iteration);

        $parsed = $this->splitRenderedPhoneValue($this->value);
        $this->selected_dial_code = $parsed['dial_code'];
        $this->local_phone_value = $parsed['number'];
    }

    private function splitRenderedPhoneValue($value): array
    {
        $value = is_scalar($value) ? trim((string) $value) : '';
        if ($value === '') {
            return array(
                'dial_code' => '',
                'number' => '',
            );
        }

        $normalized = $value;
        if (strpos($normalized, '00') === 0) {
            $normalized = '+' . preg_replace('/\D+/', '', $normalized);
            $normalized = preg_replace('/^\+00/', '+', $normalized);
        }

        if (strpos($normalized, '+') !== 0) {
            return array(
                'dial_code' => '',
                'number' => $value,
            );
        }

        $digits = preg_replace('/\D+/', '', $normalized);
        if ($digits === '') {
            return array(
                'dial_code' => '',
                'number' => '',
            );
        }

        $match = $this->findBestDialCodeMatch($digits);
        if ($match === '') {
            return array(
                'dial_code' => '',
                'number' => $value,
            );
        }

        return array(
            'dial_code' => '+' . $match,
            'number' => substr($digits, strlen($match)),
        );
    }

    private function findBestDialCodeMatch(string $digits): string
    {
        $allowed_codes = isset($this->validations['allowed_country_codes']) && is_array($this->validations['allowed_country_codes'])
            ? $this->validations['allowed_country_codes']
            : array();
        $countries = \GriffinForms\Includes\Helpers\PhoneCountries::getCountries($allowed_codes);
        $best_match = '';

        foreach ($countries as $country) {
            $dial_code = isset($country['dial_code']) ? ltrim((string) $country['dial_code'], '+') : '';
            if ($dial_code === '') {
                continue;
            }

            if (strpos($digits, $dial_code) !== 0) {
                continue;
            }

            if (strlen($dial_code) > strlen($best_match)) {
                $best_match = $dial_code;
            }
        }

        return $best_match;
    }
}
