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

namespace MailPoet\AdminPages\Pages;

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


use MailPoet\AdminPages\AssetsController;
use MailPoet\AdminPages\PageRenderer;
use MailPoet\API\JSON\ResponseBuilders\CustomFieldsResponseBuilder;
use MailPoet\Config\Localizer;
use MailPoet\CustomFields\CustomFieldsRepository;
use MailPoet\Entities\FormEntity;
use MailPoet\Entities\SegmentEntity;
use MailPoet\Form\Block;
use MailPoet\Form\FormsRepository;
use MailPoet\Form\Renderer as FormRenderer;
use MailPoet\Form\Templates\TemplateRepository;
use MailPoet\Form\Templates\Templates\Template10BelowPages;
use MailPoet\Form\Templates\Templates\Template10FixedBar;
use MailPoet\Form\Templates\Templates\Template10Popup;
use MailPoet\Form\Templates\Templates\Template10SlideIn;
use MailPoet\Form\Templates\Templates\Template10Widget;
use MailPoet\Form\Templates\Templates\Template11BelowPages;
use MailPoet\Form\Templates\Templates\Template11FixedBar;
use MailPoet\Form\Templates\Templates\Template11Popup;
use MailPoet\Form\Templates\Templates\Template11SlideIn;
use MailPoet\Form\Templates\Templates\Template11Widget;
use MailPoet\Form\Templates\Templates\Template12BelowPages;
use MailPoet\Form\Templates\Templates\Template12FixedBar;
use MailPoet\Form\Templates\Templates\Template12Popup;
use MailPoet\Form\Templates\Templates\Template12SlideIn;
use MailPoet\Form\Templates\Templates\Template12Widget;
use MailPoet\Form\Templates\Templates\Template13BelowPages;
use MailPoet\Form\Templates\Templates\Template13FixedBar;
use MailPoet\Form\Templates\Templates\Template13Popup;
use MailPoet\Form\Templates\Templates\Template13SlideIn;
use MailPoet\Form\Templates\Templates\Template13Widget;
use MailPoet\Form\Templates\Templates\Template14BelowPages;
use MailPoet\Form\Templates\Templates\Template14FixedBar;
use MailPoet\Form\Templates\Templates\Template14Popup;
use MailPoet\Form\Templates\Templates\Template14SlideIn;
use MailPoet\Form\Templates\Templates\Template14Widget;
use MailPoet\Form\Templates\Templates\Template17BelowPages;
use MailPoet\Form\Templates\Templates\Template17FixedBar;
use MailPoet\Form\Templates\Templates\Template17Popup;
use MailPoet\Form\Templates\Templates\Template17SlideIn;
use MailPoet\Form\Templates\Templates\Template17Widget;
use MailPoet\Form\Templates\Templates\Template18BelowPages;
use MailPoet\Form\Templates\Templates\Template18FixedBar;
use MailPoet\Form\Templates\Templates\Template18Popup;
use MailPoet\Form\Templates\Templates\Template18SlideIn;
use MailPoet\Form\Templates\Templates\Template18Widget;
use MailPoet\Form\Templates\Templates\Template1BelowPages;
use MailPoet\Form\Templates\Templates\Template1FixedBar;
use MailPoet\Form\Templates\Templates\Template1Popup;
use MailPoet\Form\Templates\Templates\Template1SlideIn;
use MailPoet\Form\Templates\Templates\Template1Widget;
use MailPoet\Form\Templates\Templates\Template3BelowPages;
use MailPoet\Form\Templates\Templates\Template3FixedBar;
use MailPoet\Form\Templates\Templates\Template3Popup;
use MailPoet\Form\Templates\Templates\Template3SlideIn;
use MailPoet\Form\Templates\Templates\Template3Widget;
use MailPoet\Form\Templates\Templates\Template4BelowPages;
use MailPoet\Form\Templates\Templates\Template4FixedBar;
use MailPoet\Form\Templates\Templates\Template4Popup;
use MailPoet\Form\Templates\Templates\Template4SlideIn;
use MailPoet\Form\Templates\Templates\Template4Widget;
use MailPoet\Form\Templates\Templates\Template6BelowPages;
use MailPoet\Form\Templates\Templates\Template6FixedBar;
use MailPoet\Form\Templates\Templates\Template6Popup;
use MailPoet\Form\Templates\Templates\Template6SlideIn;
use MailPoet\Form\Templates\Templates\Template6Widget;
use MailPoet\Form\Templates\Templates\Template7BelowPages;
use MailPoet\Form\Templates\Templates\Template7FixedBar;
use MailPoet\Form\Templates\Templates\Template7Popup;
use MailPoet\Form\Templates\Templates\Template7SlideIn;
use MailPoet\Form\Templates\Templates\Template7Widget;
use MailPoet\Form\Util\CustomFonts;
use MailPoet\Form\Util\Export;
use MailPoet\NotFoundException;
use MailPoet\Router\Endpoints\FormPreview;
use MailPoet\Router\Router;
use MailPoet\Segments\SegmentsSimpleListRepository;
use MailPoet\Settings\Pages;
use MailPoet\Settings\UserFlagsController;
use MailPoet\WP\AutocompletePostListLoader as WPPostListLoader;
use MailPoet\WP\Functions as WPFunctions;

class FormEditor {
  /** @var PageRenderer */
  private $pageRenderer;

  /** @var CustomFieldsRepository */
  private $customFieldsRepository;

  /** @var CustomFieldsResponseBuilder */
  private $customFieldsResponseBuilder;

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

  /** @var Block\Date */
  private $dateBlock;

  /** @var WPFunctions */
  private $wp;

  /** @var Localizer */
  private $localizer;

  /** @var TemplateRepository */
  private $templatesRepository;

  /** @var UserFlagsController */
  private $userFlags;

  /** @var WPPostListLoader */
  private $wpPostListLoader;

  /** @var SegmentsSimpleListRepository */
  private $segmentsListRepository;

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

  private $activeTemplates = [
    FormEntity::DISPLAY_TYPE_POPUP => [
      Template1Popup::ID,
      Template3Popup::ID,
      Template4Popup::ID,
      Template6Popup::ID,
      Template7Popup::ID,
      Template10Popup::ID,
      Template11Popup::ID,
      Template12Popup::ID,
      Template13Popup::ID,
      Template14Popup::ID,
      Template17Popup::ID,
      Template18Popup::ID,
    ],
    FormEntity::DISPLAY_TYPE_SLIDE_IN => [
      Template1SlideIn::ID,
      Template3SlideIn::ID,
      Template4SlideIn::ID,
      Template6SlideIn::ID,
      Template7SlideIn::ID,
      Template10SlideIn::ID,
      Template11SlideIn::ID,
      Template12SlideIn::ID,
      Template13SlideIn::ID,
      Template14SlideIn::ID,
      Template17SlideIn::ID,
      Template18SlideIn::ID,
    ],
    FormEntity::DISPLAY_TYPE_FIXED_BAR => [
      Template1FixedBar::ID,
      Template3FixedBar::ID,
      Template4FixedBar::ID,
      Template6FixedBar::ID,
      Template7FixedBar::ID,
      Template10FixedBar::ID,
      Template11FixedBar::ID,
      Template12FixedBar::ID,
      Template13FixedBar::ID,
      Template14FixedBar::ID,
      Template17FixedBar::ID,
      Template18FixedBar::ID,
    ],
    FormEntity::DISPLAY_TYPE_BELOW_POST => [
      Template1BelowPages::ID,
      Template3BelowPages::ID,
      Template4BelowPages::ID,
      Template6BelowPages::ID,
      Template7BelowPages::ID,
      Template10BelowPages::ID,
      Template11BelowPages::ID,
      Template12BelowPages::ID,
      Template13BelowPages::ID,
      Template14BelowPages::ID,
      Template17BelowPages::ID,
      Template18BelowPages::ID,
    ],
    FormEntity::DISPLAY_TYPE_OTHERS => [
      Template1Widget::ID,
      Template3Widget::ID,
      Template4Widget::ID,
      Template6Widget::ID,
      Template7Widget::ID,
      Template10Widget::ID,
      Template11Widget::ID,
      Template12Widget::ID,
      Template13Widget::ID,
      Template14Widget::ID,
      Template17Widget::ID,
      Template18Widget::ID,
    ],
  ];

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

  public function __construct(
    AssetsController $assetsController,
    PageRenderer $pageRenderer,
    CustomFieldsRepository $customFieldsRepository,
    CustomFieldsResponseBuilder $customFieldsResponseBuilder,
    FormRenderer $formRenderer,
    Block\Date $dateBlock,
    WPFunctions $wp,
    Localizer $localizer,
    UserFlagsController $userFlags,
    WPPostListLoader $wpPostListLoader,
    TemplateRepository $templateRepository,
    FormsRepository $formsRepository,
    SegmentsSimpleListRepository $segmentsListRepository
  ) {
    $this->assetsController = $assetsController;
    $this->pageRenderer = $pageRenderer;
    $this->customFieldsRepository = $customFieldsRepository;
    $this->customFieldsResponseBuilder = $customFieldsResponseBuilder;
    $this->formRenderer = $formRenderer;
    $this->dateBlock = $dateBlock;
    $this->wp = $wp;
    $this->localizer = $localizer;
    $this->templatesRepository = $templateRepository;
    $this->userFlags = $userFlags;
    $this->wpPostListLoader = $wpPostListLoader;
    $this->segmentsListRepository = $segmentsListRepository;
    $this->formsRepository = $formsRepository;
  }

  public function render() {
    if (!isset($_GET['id']) && !isset($_GET['action']) && !isset($_GET['template_id'])) {
      $this->renderTemplateSelection();
      return;
    }
    if (is_string($_GET['template_id'] ?? null)) {
      $template = $this->templatesRepository->getFormTemplate(sanitize_text_field(wp_unslash($_GET['template_id'])));
      $form = $template->toFormEntity();
    } else {
      // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- is_numeric guard plus int cast is the sanitization
      $idParam = $_GET['id'] ?? null;
      $form = $this->getFormData(is_numeric($idParam) ? (int)$idParam : 0);
    }
    $customFields = $this->customFieldsRepository->findAllActive();
    if (!$form instanceof FormEntity) {
      throw new NotFoundException('Form does not exist');
    }
    $dateTypes = $this->dateBlock->getDateTypes();
    $data = [
      'form' => $form->toArray(),
      'form_exports' => [
          'php' => Export::get('php'),
          'iframe' => Export::get('iframe'),
          'shortcode' => Export::get('shortcode'),
      ],
      'segments' => $this->segmentsListRepository->getListWithSubscribedSubscribersCounts([SegmentEntity::TYPE_DEFAULT]),
      'styles' => $this->formRenderer->getCustomStyles($form),
      'date_types' => array_map(function ($label, $value) {
        return [
          'label' => $label,
          'value' => $value,
        ];
      }, $dateTypes, array_keys($dateTypes)),
      'date_formats' => $this->dateBlock->getDateFormats(),
      'month_names' => $this->dateBlock->getMonthNames(),
      'custom_fields' => $this->customFieldsResponseBuilder->buildBatch($customFields),
      'editor_tutorial_seen' => $this->userFlags->get('form_editor_tutorial_seen'),
      'preview_page_url' => $this->getPreviewPageUrl(),
      'custom_fonts' => CustomFonts::FONTS,
      'editor_color_palette' => $this->getEditorColorPalette(),
      'translations' => $this->getGutenbergScriptsTranslations(),
      'posts' => $this->wpPostListLoader->getPosts(),
      'pages' => $this->wpPostListLoader->getPages(),
      'categories' => $this->wpPostListLoader->getCategories(),
      'tags' => $this->wpPostListLoader->getTags(),
      'products' => $this->wpPostListLoader->getProducts(),
      'product_categories' => $this->wpPostListLoader->getWooCommerceCategories(),
      'product_tags' => $this->wpPostListLoader->getWooCommerceTags(),
      'is_administrator' => $this->wp->currentUserCan('administrator'),
      'theme_support_widgets' => $this->wp->wpGetThemeSupport('widgets'),
      'theme_support_fse' => $this->wp->wpGetTheme()->is_block_theme(),
    ];
    $this->wp->wpEnqueueMedia();
    $this->assetsController->setupFormEditorDependencies();
    $this->pageRenderer->displayPage('form/editor.html', $data);
  }

  private function getEditorColorPalette(): array {
    $palette = $this->normalizeColorPalette(
      $this->wp->wpGetGlobalSettings(['color', 'palette', 'theme'])
    );
    if ($palette) {
      return $palette;
    }
    return $this->normalizeColorPalette(
      $this->wp->wpGetThemeSupport('editor-color-palette')
    );
  }

  private function normalizeColorPalette($palette): array {
    if (!is_array($palette)) {
      return [];
    }
    if (
      isset($palette[0])
      && is_array($palette[0])
      && !isset($palette[0]['color'])
    ) {
      $palette = $palette[0];
    }

    $normalizedPalette = [];
    foreach ($palette as $color) {
      if (!is_array($color)) {
        continue;
      }
      $name = isset($color['name']) && is_scalar($color['name'])
        ? $this->wp->sanitizeTextField((string)$color['name'])
        : '';
      $slug = isset($color['slug']) && is_scalar($color['slug'])
        ? $this->wp->sanitizeKey((string)$color['slug'])
        : '';
      $colorValue = isset($color['color']) && is_scalar($color['color'])
        ? $this->wp->sanitizeTextField((string)$color['color'])
        : '';
      if (!$name || !$slug || !$colorValue) {
        continue;
      }
      $normalizedPalette[] = [
        'name' => $name,
        'slug' => $slug,
        'color' => $colorValue,
      ];
    }
    return $normalizedPalette;
  }

  public function renderTemplateSelection() {
    $templatesData = [];
    foreach ($this->activeTemplates as $formType => $templateIds) {
      $templateForms = $this->templatesRepository->getFormTemplates($this->activeTemplates[$formType]);
      $templatesData[$formType] = [];
      foreach ($templateForms as $templateId => $form) {
        $templatesData[$formType][] = [
          'id' => $templateId,
          'name' => $form->getName(),
          'thumbnail' => $form->getThumbnailUrl(),
        ];
      }
    }
    $data = [
      'templates' => $templatesData,
    ];
    $this->assetsController->setupFormEditorDependencies();
    $this->pageRenderer->displayPage('form/template_selection.html', $data);
  }

  private function getPreviewPageUrl() {
    $mailpoetPage = Pages::getMailPoetPage(Pages::PAGE_SUBSCRIPTIONS);
    if (!$mailpoetPage) {
      return null;
    }
    $url = $this->wp->getPermalink($mailpoetPage);
    $params = [
      Router::NAME,
      'endpoint=' . FormPreview::ENDPOINT,
      'action=' . FormPreview::ACTION_VIEW,
    ];
    $url .= (parse_url($url, PHP_URL_QUERY) ? '&' : '?') . join('&', $params);
    return $url;
  }

  /**
   * JS Translations are distributed and loaded per script. We can't use wp_set_script_translations
   * because translation filename is determined based on script filename and path.
   * This function loads JSON files with Gutenberg script's translations distributed within WordPress.
   * Implemented based on load_script_textdomain function
   * @see https://developer.wordpress.org/reference/functions/load_script_textdomain/
   * @return string[]
   */
  private function getGutenbergScriptsTranslations() {
    $locale = $this->localizer->locale();
    if (!$locale) {
      return [];
    }
    // List of scripts - relative path to translations directory (default: wp-content/languages)
    $translationsToLoad = [
      'wp-includes/js/dist/blocks.js',
      'wp-includes/js/dist/components.js',
      'wp-includes/js/dist/block-editor.js',
      'wp-includes/js/dist/block-library.js',
      'wp-includes/js/dist/editor.js',
      'wp-includes/js/dist/media-utils.js',
      'wp-includes/js/dist/format-library.js',
      'wp-includes/js/dist/edit-post.js',
    ];

    $translations = [];
    foreach ($translationsToLoad as $translation) {
      $file = WP_LANG_DIR . '/' . $locale . '-' . md5($translation) . '.json';
      if (!file_exists($file)) {
        continue;
      }
      $translationsData = file_get_contents($file);
      if ($translationsData) {
        $translations[] = $translationsData;
      }
    }
    return $translations;
  }

  private function getFormData(int $id): ?FormEntity {
    $form = $this->formsRepository->findOneById($id);
    if (!$form instanceof FormEntity) {
      return null;
    }
    $form->setStyles($this->formRenderer->getCustomStyles($form));
    // Use empty settings in case they are corrupted or missing
    if (!is_array($form->getSettings())) {
      $initialFormTemplate = $this->templatesRepository->getFormTemplate(TemplateRepository::INITIAL_FORM_TEMPLATE);
      $form->setSettings($initialFormTemplate->getSettings());
    }
    return $form;
  }
}
