<?php // phpcs:ignore SlevomatCodingStandard.TypeHints.DeclareStrictTypes.DeclareStrictTypesMissing

namespace MailPoet\Form;

if (!defined('ABSPATH')) exit;


use MailPoet\API\JSON\API;
use MailPoet\Config\Env;
use MailPoet\Config\RendererFactory;
use MailPoet\DI\ContainerWrapper;
use MailPoet\Entities\FormEntity;
use MailPoet\Form\Renderer as FormRenderer;
use MailPoet\Form\Util\CustomFonts;
use MailPoet\Settings\SettingsController;
use MailPoet\WP\Functions as WPFunctions;

// phpcs:disable Generic.Files.InlineHTML
class Widget extends \WP_Widget {
  private $renderer;
  private $wp;

  /** @var AssetsController */
  private $assetsController;

  /** @var FormRenderer */
  private $formRenderer;

  /** @var FormsRepository */
  private $formsRepository;

  /** @var CustomFonts */
  private $customFonts;

  /** @var SettingsController */
  private $settings;

  public function __construct() {
    parent::__construct(
      'mailpoet_form',
      __('MailPoet Form', 'mailpoet'),
      ['description' => __('Add a newsletter subscription form', 'mailpoet')]
    );
    $this->wp = new WPFunctions;

    $this->renderer = (new RendererFactory())->getRenderer();
    $this->settings = SettingsController::getInstance();
    $this->assetsController = new AssetsController($this->wp, $this->renderer, $this->settings);
    $this->formRenderer = ContainerWrapper::getInstance()->get(FormRenderer::class);
    $this->formsRepository = ContainerWrapper::getInstance()->get(FormsRepository::class);
    $this->customFonts = ContainerWrapper::getInstance()->get(CustomFonts::class);

    if (!$this->wp->isAdmin()) {
      $this->setupIframe();
    } else {
      WPFunctions::get()->addAction('widgets_admin_page', [
        $this->assetsController,
        'setupAdminWidgetPageDependencies',
      ]);
    }
  }

  public function setupIframe() {
    $formId = isset($_GET['mailpoet_form_iframe']) && is_numeric($_GET['mailpoet_form_iframe']) ? (int)$_GET['mailpoet_form_iframe'] : 0;
    if (!$formId || !$this->formsRepository->findOneById($formId)) return;

    $formHtml = $this->widget(
      [
        'form' => $formId,
        'form_type' => 'iframe',
      ]
    );

    $scripts = $this->assetsController->printScripts();

    // language attributes
    $languageAttributes = [];
    $isRtl = (bool)(function_exists('is_rtl') && WPFunctions::get()->isRtl());

    if ($isRtl) {
      $languageAttributes[] = 'dir="rtl"';
    }

    if ($this->wp->getOption('html_type') === 'text/html') {
      $languageAttributes[] = sprintf('lang="%s"', WPFunctions::get()->getBloginfo('language'));
    }

    $languageAttributes = WPFunctions::get()->applyFilters(
      'language_attributes',
      implode(' ', $languageAttributes)
    );

    $data = [
      'language_attributes' => $languageAttributes,
      'scripts' => $scripts,
      'form' => $formHtml,
      'mailpoet_form' => [
        'ajax_url' => WPFunctions::get()->adminUrl('admin-ajax.php', 'absolute'),
        'is_rtl' => $isRtl,
        'collect_subscriber_timezones' => $this->settings->isSettingEnabled('collect_subscriber_timezones.enabled'),
      ],
      'fonts_link' => $this->customFonts->generateHtmlCustomFontLink(),
      'mailpoet_public_css_url' => Env::$assetsUrl . '/dist/css/' . $this->renderer->getCssAsset('mailpoet-public.css'),
    ];

    try {
      // We control the template and the data is sanitized
      // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
      echo $this->renderer->render('form/iframe.html', $data);
    } catch (\Exception $e) {
      echo esc_html($e->getMessage());
    }

    exit();
  }

  /**
   * Save the new widget's title.
   */
  public function update($newInstance, $oldInstance) {
    $instance = $oldInstance;
    $instance['title'] = strip_tags(is_string($newInstance['title']) ? $newInstance['title'] : '');
    $instance['form'] = is_numeric($newInstance['form']) ? (int)$newInstance['form'] : null;
    return $instance;
  }

  /**
   * Output the widget's option form.
   */
  public function form($instance) {
    $instance = WPFunctions::get()->wpParseArgs(
      (array)$instance,
      [
        'title' => __('Subscribe to Our Newsletter', 'mailpoet'),
      ]
    );

    $formEditUrl = WPFunctions::get()->adminUrl('admin.php?page=mailpoet-form-editor-template-selection');

    // set title
    $title = isset($instance['title']) ? strip_tags($instance['title']) : '';

    // set form
    $selectedForm = isset($instance['form']) ? (int)($instance['form']) : 0;

    // get forms list
    $forms = $this->formsRepository->findBy(['deletedAt' => null], ['name' => 'asc']);
    ?><p>
      <label for="<?php echo esc_attr($this->get_field_id('title')) ?>"><?php echo esc_html(__('Title:', 'mailpoet')); ?></label>
      <input
        type="text"
        class="widefat"
        id="<?php echo esc_attr($this->get_field_id('title')) ?>"
        name="<?php echo esc_attr($this->get_field_name('title')); ?>"
        value="<?php echo esc_attr($title); ?>"
      />
    </p>
    <p>
      <select class="widefat" id="<?php echo esc_attr($this->get_field_id('form')) ?>" name="<?php echo esc_attr($this->get_field_name('form')); ?>">
        <?php
        // Select the first one from the list if none selected
        if ($selectedForm === 0 && !empty($forms)) $selectedForm = $forms[0]->getId();
        foreach ($forms as $form) {
          $formName = $form->getName() ? $this->wp->escHtml($form->getName()) : '(' . _x('no name', 'fallback for forms without a name in a form list', 'mailpoet') . ')';
          $formName .= $form->getStatus() === FormEntity::STATUS_DISABLED ? ' (' . __('inactive', 'mailpoet') . ')' : '';
          ?>
        <option value="<?php echo esc_attr((string)$form->getId()); ?>" <?php echo ($selectedForm === $form->getId()) ? 'selected="selected"' : ''; ?>><?php echo esc_html($formName); ?></option>
        <?php } ?>
      </select>
    </p>
    <p>
      <a href="<?php echo esc_url($formEditUrl); ?>" target="_blank" class="mailpoet_form_new"><?php echo esc_html(__('Create a new form', 'mailpoet')); ?></a>
    </p>
    <?php
    return '';
  }

  /**
   * @param array{form?: int|string, form_type?: string, before_widget?: string, after_widget?: string, before_title?: string, after_title?: string } $args Widget arguments.
   * Output the widget itself.
   */
  public function widget($args, $instance = null) {
    $this->assetsController->setupFrontEndDependencies();

    $beforeWidget = !empty($args['before_widget']) ? $args['before_widget'] : '';
    $afterWidget = !empty($args['after_widget']) ? $args['after_widget'] : '';
    $beforeTitle = !empty($args['before_title']) ? $args['before_title'] : '';
    $afterTitle = !empty($args['after_title']) ? $args['after_title'] : '';

    if ($instance === null) {
      $instance = $args;
    }

    $title = $this->wp->applyFilters(
      'widget_title',
      !empty($instance['title']) ? $instance['title'] : '',
      $instance,
      $this->id_base // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
    );

    // get form
    if (!empty($instance['form'])) {
      $form = $this->formsRepository->findOneById($instance['form']);
    } else {
      // Backwards compatibility for MAILPOET-3847
      // Get first non deleted form
      $forms = $this->formsRepository->findBy(['deletedAt' => null], ['name' => 'asc']);
      if (empty($forms)) return '';
      $form = $forms[0];
    }

    if (!$form) return '';
    if ($form->getDeletedAt()) return '';
    if ($form->getStatus() !== FormEntity::STATUS_ENABLED) return '';

    $formType = 'widget';
    if (
      isset($instance['form_type']) && in_array(
        $instance['form_type'],
        [
          'html',
          'php',
          'iframe',
          'shortcode',
        ]
      )
    ) {
      $formType = $instance['form_type'];
    }

    $body = (!empty($form->getBody()) ? $form->getBody() : []);
    $output = '';
    $settings = $form->getSettings();

    if (!empty($body) && is_array($settings)) {
      $idBase = is_string($this->id_base) ? $this->id_base : 'mailpoet_form'; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
      $formId = $idBase . '_' . $form->getId();
      $data = [
        'form_html_id' => $formId,
        'form_id' => $form->getId(),
        'form_type' => $formType,
        'form_success_message' => $settings['success_message'],
        'title' => $title,
        'styles' => $this->formRenderer->renderStyles($form, '#' . $formId, FormEntity::DISPLAY_TYPE_OTHERS),
        'html' => $this->formRenderer->renderHTML($form),
        'before_widget' => $beforeWidget,
        'after_widget' => $afterWidget,
        'before_title' => $beforeTitle,
        'after_title' => $afterTitle,
      ];

      // (POST) non ajax success/error variables
      $data['success'] = (
        isset($_GET['mailpoet_success'])
        && is_numeric($_GET['mailpoet_success'])
        && (int)$_GET['mailpoet_success'] === $form->getId()
      );
      $data['error'] = (
        isset($_GET['mailpoet_error'])
        && is_numeric($_GET['mailpoet_error'])
        && (int)$_GET['mailpoet_error'] === $form->getId()
      );

      // generate security token
      $data['token'] = $this->wp->wpCreateNonce('mailpoet_token');

      // add API version
      $data['api_version'] = API::CURRENT_VERSION;

      // render form
      $renderer = (new RendererFactory())->getRenderer();
      try {
        $output = $renderer->render('form/front_end_form.html', $data);
        $output = WPFunctions::get()->doShortcode($output);

        // Define the exact keys we want to keep for the filter context
        $allowed_keys = [
            'form_html_id' => null,
            'form_id' => null,
            'form_type' => null,
            'form_success_message' => null,
            'title' => null,
            'styles' => null,
            'html' => null,
            'before_widget' => null,
            'after_widget' => null,
            'before_title' => null,
            'after_title' => null,
        ];

        // Create a new context array for the filter, containing only the allowed keys.
        // This automatically excludes 'success', 'error', 'token', and 'api_version'.
        $filter_context_data = array_intersect_key($data, $allowed_keys);
        
        /**
        * Filters the rendered MailPoet form HTML after shortcodes are processed.
        *
        * @since TBD Added the $data context parameter.
        *
        * @param string                $output Rendered form HTML.
        * @param array<string, mixed>  $filter_context_data   Rendering context (form id, type, styles, and more).
        * @return string Filtered HTML.
        */
        $output = $this->wp->applyFilters('mailpoet_form_widget_post_process', $output, $filter_context_data);
      } catch (\Exception $e) {
        $output = $e->getMessage();
      }
    }

    if ($formType === 'widget') {
      /** @var string $output */
      // We control the template and the data is sanitized
      // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
      echo $output;
    } else {
      return $output;
    }
  }
}
