<?php

namespace GriffinForms\Frontend\Html\Forms\Fields;

final class TermsConditions extends \GriffinForms\Frontend\Html\Forms\Fields\Format
{
    /**
     * Class name presets for terms field elements.
     *
     * Keys support optional 'bs' (Bootstrap fallback) and 'theme' (append when themed).
     *
     * @var array<string, array<string, string>>
     */
    protected $class_presets = array(
        'checkbox_label'   => array('base' => 'form-check-label'),
        'checkbox_wrapper' => array(
            'base' => 'griffinforms-terms-check d-flex align-items-center',
            'bs'   => 'form-check mb-2',
        ),
        'subtext'          => array(
            'base' => 'small mt-2 griffinforms-terms-subtext',
            'bs'   => 'text-muted',
        ),
        'modal_dialog'     => array(
            'base' => 'modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable griffinforms-terms-modal-dialog',
        ),
        'modal_content'    => array('base' => 'modal-content griffinforms-terms-modal-surface'),
        'modal_title'      => array(
            'base' => 'modal-title griffinforms-terms-modal-title',
            'bs'   => 'fw-medium',
        ),
        'modal_body'       => array(
            'base' => 'modal-body griffinforms-terms-modal-body',
            'bs'   => 'small',
        ),
        'modal_footer'     => array(
            'base' => 'modal-footer d-flex justify-content-center griffinforms-terms-modal-footer',
        ),
        'modal_decline'    => array(
            'base' => 'griffinforms-terms-decline',
            'bs'   => 'button button-secondary wp-element-button wp-block-button__link',
        ),
        'modal_accept'     => array(
            'base' => 'griffinforms-terms-accept',
            'bs'   => 'button button-primary wp-element-button wp-block-button__link',
        ),
    );

    /**
     * Resolve class string for a given preset.
     */
    protected function resolvePresetClasses($key, $is_theme_set)
    {
        if (!isset($this->class_presets[$key])) {
            return '';
        }

        $preset = $this->class_presets[$key];
        $classes = array($preset['base']);

        if ($is_theme_set) {
            if (!empty($preset['theme'])) {
                $classes[] = $preset['theme'];
            }
        } elseif (!empty($preset['bs'])) {
            $classes[] = $preset['bs'];
        }

        return trim(implode(' ', array_filter($classes)));
    }

    /**
     * Build rendering context for terms field output.
     *
     * @param int $iteration
     * @return array<string, mixed>
     */
    protected function buildRenderContext($iteration)
    {
        $settings = is_array($this->field_settings) ? $this->field_settings : array();

        $checkbox_label = isset($settings['checkbox_label']) && $settings['checkbox_label'] !== ''
            ? $settings['checkbox_label']
            : __('I agree to the Terms and Conditions', 'griffinforms-form-builder');

        $anchor_text = isset($settings['anchor_text']) && $settings['anchor_text'] !== ''
            ? $settings['anchor_text']
            : __('Read Terms & Conditions', 'griffinforms-form-builder');

        $subtext = array_key_exists('subtext', $settings) ? (string) $settings['subtext'] : '';

        $link_type = isset($settings['link_type']) && in_array($settings['link_type'], array('page', 'post'), true)
            ? $settings['link_type']
            : 'page';

        $page_id = isset($settings['page_id']) ? absint($settings['page_id']) : 0;
        $post_id = isset($settings['post_id']) ? absint($settings['post_id']) : 0;
        $target_id = $link_type === 'post' ? $post_id : $page_id;
        $target_url = $target_id ? get_permalink($target_id) : '';

        $open_modal = !empty($settings['open_in_modal']);

        $accept_label = isset($settings['accept_label']) && $settings['accept_label'] !== ''
            ? $settings['accept_label']
            : __('I Accept', 'griffinforms-form-builder');

        $decline_label = isset($settings['decline_label']) && $settings['decline_label'] !== ''
            ? $settings['decline_label']
            : __('Do Not Accept', 'griffinforms-form-builder');

        $checkbox_id = $this->html_ids['formcontrol'] . '-' . $iteration . '-terms';
        $modal_id = 'griffinforms-terms-modal-' . $this->id . '-' . $iteration;

        $is_checked = in_array($this->value, array('1', 1, 'accepted'), true);

        $disabled_attr = $this->disabledAttr();
        $readonly_attr = '';
        if ($this->isReadonly() && !$this->isDisabled()) {
            $readonly_attr = ' disabled="disabled"';
        }

        $is_theme_set = !empty($this->data['is_theme_set']);

        return array(
            'checkbox_label' => $checkbox_label,
            'anchor_text' => $anchor_text,
            'subtext' => $subtext,
            'link_type' => $link_type,
            'target_id' => $target_id,
            'target_url' => $target_url,
            'open_modal' => $open_modal,
            'accept_label' => $accept_label,
            'decline_label' => $decline_label,
            'checkbox_id' => $checkbox_id,
            'modal_id' => $modal_id,
            'is_checked' => $is_checked,
            'disabled_attr' => $disabled_attr,
            'readonly_attr' => $readonly_attr,
            'is_theme_set' => $is_theme_set,
            'hidden_value' => $is_checked ? '1' : '',
            'formpage' => absint($this->data['formpage']),
        );
    }

    /**
     * Render the checkbox container, input, and label.
     *
     * @param array<string, mixed> $context
     * @return string
     */
    protected function renderCheckbox(array $context)
    {
        $wrapper_classes = $this->resolvePresetClasses('checkbox_wrapper', $context['is_theme_set']);
        $label_classes = $this->resolvePresetClasses('checkbox_label', $context['is_theme_set']);

        $html = '<div class="' . esc_attr($wrapper_classes) . '">';
        $html .= '<input type="checkbox" class="form-check-input gf-checkbox-radio-input ' . esc_attr($this->html_ids['formcontrol']) . '-checkbox"';
        $html .= ' id="' . esc_attr($context['checkbox_id']) . '"';

        if ($context['is_checked']) {
            $html .= ' checked="checked"';
        }

        $html .= $context['disabled_attr'];
        $html .= $context['readonly_attr'];
        $html .= '>';
        $html .= '<label class="' . esc_attr($label_classes) . '" for="' . esc_attr($context['checkbox_id']) . '" data-gf-element="checkbox-label">';
        $html .= esc_html($context['checkbox_label']);
        $html .= '</label>';
        $html .= '</div>';

        return $html;
    }

    /**
     * Render the link or modal trigger for terms content.
     *
     * @param array<string, mixed> $context
     * @return string
     */
    protected function renderTermsLink(array $context)
    {
        if (empty($context['target_url']) || $context['anchor_text'] === '') {
            return '';
        }

        if (!$context['open_modal']) {
            $html = '<div>';
            $html .= '<a href="' . esc_url($context['target_url']) . '" target="_blank" rel="noopener noreferrer">';
            $html .= esc_html($context['anchor_text']);
            $html .= '</a>';
            $html .= '</div>';
            return $html;
        }

        $html = '<div>';
        $html .= '<a href="#' . esc_attr($context['modal_id']) . '" data-bs-toggle="modal" data-bs-target="#' . esc_attr($context['modal_id']) . '"';
        $html .= ' data-gf-terms-field="' . absint($this->id) . '"';
        $html .= '>';
        $html .= esc_html($context['anchor_text']);
        $html .= '</a>';
        $html .= '</div>';
        return $html;
    }

    /**
     * Render optional subtext helper.
     *
     * @param array<string, mixed> $context
     * @return string
     */
    protected function renderSubtext(array $context)
    {
        if ($context['subtext'] === '') {
            return '';
        }

        $subtext_classes = $this->resolvePresetClasses('subtext', $context['is_theme_set']);

        $html = '<div class="' . esc_attr($subtext_classes) . '">';
        $html .= esc_html($context['subtext']);
        $html .= '</div>';

        return $html;
    }

    /**
     * Render hidden value used for submissions.
     *
     * @param array<string, mixed> $context
     * @return string
     */
    protected function renderHiddenInput(array $context)
    {
        $html = '<input type="hidden" class="' . esc_attr($this->html_ids['formcontrol']) . '"';
        $html .= ' value="' . esc_attr($context['hidden_value']) . '"';
        $html .= ' data-gf-formpage="' . $context['formpage'] . '">';

        return $html;
    }

    protected function formControl($iteration = 0)
    {
        $context = $this->buildRenderContext($iteration);

        $html = '<div class="griffinforms-terms-field">';
        $html .= $this->renderCheckbox($context);
        $html .= $this->renderTermsLink($context);
        $html .= $this->renderSubtext($context);
        $html .= $this->renderHiddenInput($context);

        if ($context['open_modal'] && $context['target_url']) {
            $nonce = wp_create_nonce('griffinforms_fetch_terms');
            $html .= $this->modalHtml(
                $context['modal_id'],
                $context['checkbox_id'],
                $context['link_type'],
                $context['target_id'],
                $context['accept_label'],
                $context['decline_label'],
                $nonce
            );
        }

        $html .= '</div>';

        return $html;
    }

    protected function modalHtml($modal_id, $checkbox_id, $link_type, $target_id, $accept_label, $decline_label, $nonce)
    {
        $title = $this->item->getProp('heading');

        if ($title === '') {
            $title = __('Terms and Conditions', 'griffinforms-form-builder');
        }

        $is_theme_set = !empty($this->data['is_theme_set']);

        $form_id = isset($this->data['form']) ? absint($this->data['form']) : 0;

        $modal_dialog_classes = $this->resolvePresetClasses('modal_dialog', $is_theme_set);
        $modal_content_classes = $this->resolvePresetClasses('modal_content', $is_theme_set);
        $modal_title_classes = $this->resolvePresetClasses('modal_title', $is_theme_set);
        $modal_body_classes = $this->resolvePresetClasses('modal_body', $is_theme_set);
        $modal_footer_classes = $this->resolvePresetClasses('modal_footer', $is_theme_set);

        $html = '<div class="modal fade griffinforms-terms-modal" id="' . esc_attr($modal_id) . '" tabindex="-1" aria-hidden="true"';
        $html .= ' data-gf-terms-type="' . esc_attr($link_type) . '"';
        $html .= ' data-gf-terms-id="' . absint($target_id) . '"';
        $html .= ' data-gf-terms-field="' . absint($this->id) . '"';
        $html .= ' data-gf-fetch-nonce="' . esc_attr($nonce) . '"';
        if ($is_theme_set && $form_id > 0) {
            $html .= ' data-gf-form-id="' . $form_id . '"';
        }
        $html .= '>';
        $html .= '<div class="' . esc_attr($modal_dialog_classes) . '">';
        $html .= '<div class="' . esc_attr($modal_content_classes) . '"';
        if ($is_theme_set) {
            $html .= ' data-gf-page-type="page"';
            if ($form_id > 0) {
                $html .= ' data-gf-form-id="' . $form_id . '"';
            }
        }
        $html .= '>';
        $modal_header_classes = 'modal-header';
        if ($is_theme_set) {
            $modal_header_classes .= ' border-0';
        }
        $html .= '<div class="' . esc_attr($modal_header_classes) . '">';
        $html .= '<h5 class="' . esc_attr($modal_title_classes) . '" data-gf-element="page-heading">' . esc_html($title) . '</h5>';
        $html .= '<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="' . esc_attr__('Close', 'griffinforms-form-builder') . '"></button>';
        $html .= '</div>';
        $html .= '<div class="' . esc_attr($modal_body_classes) . '" data-loaded="0" style="max-height:60vh; overflow-y:auto;">';
        $content_attributes = 'class="griffinforms-terms-modal-content"';
        if ($is_theme_set) {
            $content_attributes .= ' data-gf-element="page-description"';
        }
        $html .= '<div ' . $content_attributes . '>';
        $html .= '<div class="text-center py-5">';
        $html .= '<div class="spinner-border text-secondary" role="status">';
        $html .= '<span class="visually-hidden">' . esc_html__('Loading...', 'griffinforms-form-builder') . '</span>';
        $html .= '</div>';
        $html .= '</div>';
        $html .= '</div>';
        $html .= '</div>';
        $modal_footer_wrapper_classes = $modal_footer_classes;
        if ($is_theme_set) {
            $modal_footer_wrapper_classes .= ' border-0';
        }
        $html .= '<div class="' . esc_attr(trim($modal_footer_wrapper_classes)) . '">';

        $decline_button_classes = $this->resolvePresetClasses('modal_decline', $is_theme_set);
        $accept_button_classes = $this->resolvePresetClasses('modal_accept', $is_theme_set);

        $html .= '<button type="button" style="min-width: 100px;" class="' . esc_attr($decline_button_classes) . '"';
        $html .= ' data-bs-dismiss="modal" data-gf-checkbox="#' . esc_attr($checkbox_id) . '" data-gf-button-type="previous">';
        $html .= esc_html($decline_label);
        $html .= '</button>';

        $html .= '<button type="button" style="min-width: 100px;" class="' . esc_attr($accept_button_classes) . '"';
        $html .= ' data-bs-dismiss="modal" data-gf-checkbox="#' . esc_attr($checkbox_id) . '" data-gf-button-type="submit">';
        $html .= esc_html($accept_label);
        $html .= '</button>';
        $html .= '</div>';
        $html .= '</div>';
        $html .= '</div>';
        $html .= '</div>';

        return $html;
    }

    protected function subItemJs()
    {
        $field_id = absint($this->id);
        $modal_selector = '.griffinforms-terms-modal[data-gf-terms-field="' . $field_id . '"]';
        $checkbox_selector = '.' . $this->html_ids['formcontrol'] . '-checkbox';
        $hidden_selector = '.' . $this->html_ids['formcontrol'];
        $ajax_url = esc_url(admin_url('admin-ajax.php'));
        $fallback_message = __('Unable to load terms content.', 'griffinforms-form-builder');

        ob_start();
        ?>
        const termsCheckboxSelector = <?php echo wp_json_encode($checkbox_selector); ?>;
        const termsHiddenSelector = <?php echo wp_json_encode($hidden_selector); ?>;
        const termsModalSelector = <?php echo wp_json_encode($modal_selector); ?>;
        const termsFieldId = <?php echo $field_id; ?>;
        const termsAjaxUrl = <?php echo wp_json_encode($ajax_url); ?>;
        const termsFallbackMessage = <?php echo wp_json_encode($fallback_message); ?>;

        function updateTermsHidden(checkboxEl) {
            const $checkbox = $(checkboxEl);
            const container = $checkbox.closest(".griffinforms-formcontrol-container");
            const hiddenInput = container.find(termsHiddenSelector);
            const isChecked = $checkbox.is(":checked");

            hiddenInput.val(isChecked ? "1" : "");
            triggerFieldChanged({ element: checkboxEl, value: hiddenInput.val() });
        }

        $(document).on("change", termsCheckboxSelector, function() {
            updateTermsHidden(this);
        });

        $(termsCheckboxSelector).each(function() {
            updateTermsHidden(this);
        });

        $(document).on("click", ".griffinforms-terms-accept, .griffinforms-terms-decline", function() {
            const targetSelector = $(this).attr("data-gf-checkbox");

            if (!targetSelector) {
                return;
            }

            const checkbox = $(targetSelector);

            if (!checkbox.length) {
                return;
            }

            const shouldCheck = $(this).hasClass("griffinforms-terms-accept");
            checkbox.prop("checked", shouldCheck);
            updateTermsHidden(checkbox.get(0));
        });

        $(document).on("show.bs.modal", termsModalSelector, function() {
            const modal = $(this);
            const body = modal.find(".griffinforms-terms-modal-body");
            const contentWrapper = body.find(".griffinforms-terms-modal-content");

            if (Number(body.data("loaded")) === 1) {
                return;
            }

            const requestData = {
                action: "griffinformsFetchTerms",
                nonce: modal.data("gf-fetch-nonce"),
                field_id: termsFieldId,
                source_type: modal.data("gf-terms-type"),
                source_id: modal.data("gf-terms-id")
            };

            $.post(termsAjaxUrl, requestData)
                .done(function(response) {
                    let json = response;

                    if (typeof json !== "object") {
                        try {
                            json = JSON.parse(response);
                        } catch (err) {
                            json = null;
                        }
                    }

                    if (!json || !json.success || !json.content) {
                        const message = (json && json.message) ? json.message : termsFallbackMessage;
                        contentWrapper.html('<div class="text-danger">' + message + "</div>");
                        body.data("loaded", 1);
                        return;
                    }

                    contentWrapper.html(json.content);
                    body.data("loaded", 1);
                })
                .fail(function() {
                    contentWrapper.html('<div class="text-danger">' + termsFallbackMessage + "</div>");
                    body.data("loaded", 1);
                });
        });
        <?php
        echo ob_get_clean();
    }

    protected function setErrors()
    {
        $this->errors = array(
            'required',
            'unique',
            'dbValidate'
        );
    }

    protected function requiredJs($error)
    {
        echo '
            $(actionBtn).hover(function() {
                $(formControl).each(checkTermsRequired);
            });

            $(document).on("change", termsCheckboxSelector, function() {
                $(formControl).each(checkTermsRequired);
            });

            function checkTermsRequired() {
                const val = $(this).val();

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