<?php

class MailsterBlockForms {

	private $forms = array();
	private $preview_data;

	public function __construct() {

		// since 5.8
		if ( ! function_exists( 'get_allowed_block_types' ) ) {
			return;
		}

		add_action( 'init', array( &$this, 'register_post_type' ) );
		add_action( 'init', array( &$this, 'register_post_meta' ) );
		add_action( 'init', array( &$this, 'block_init' ) );
		add_action( 'rest_api_init', array( &$this, 'register_block_patterns' ) );
		add_action( 'rest_api_init', array( &$this, 'rest_api_init' ) );
		add_action( 'rest_api_init', array( &$this, 'register_settings' ) );

		add_action( 'admin_print_scripts-edit.php', array( &$this, 'overview_script_styles' ), 1 );

		add_action( 'enqueue_block_assets', array( &$this, 'block_script_styles' ) );

		add_filter( 'allowed_block_types_all', array( &$this, 'allowed_block_types' ), 9999, 2 );
		add_filter( 'block_editor_settings_all', array( &$this, 'block_editor_settings' ), PHP_INT_MAX, 2 );
		add_filter( 'block_categories_all', array( &$this, 'block_categories' ) );
		add_filter( 'use_block_editor_for_post_type', array( &$this, 'force_block_editor' ), PHP_INT_MAX, 2 );

		add_filter( 'manage_mailster-form_posts_columns', array( &$this, 'columns' ), 1 );
		add_action( 'manage_mailster-form_posts_custom_column', array( &$this, 'custom_column' ), 10, 2 );
		add_filter( 'wp_list_table_class_name', array( &$this, 'wp_list_table_class_name' ), 10, 2 );

		add_filter( 'template_redirect', array( &$this, 'prepare_forms' ) );

		add_filter( 'render_block_core/button', array( &$this, 'maybe_add_form_for_button' ), 10, 2 );

		add_action( 'save_post_mailster-form', array( &$this, 'clear_cache' ) );
		add_action( 'save_post_page', array( &$this, 'maybe_set_homepage' ), 10, 3 );
		add_action( 'switch_theme', array( &$this, 'clear_inline_style' ) );

		add_filter( 'block_editor_settings_all', array( &$this, 'disable_block_unlocks' ), 10, 2 );

		add_shortcode( 'mailster_form', array( &$this, 'shortcode' ) );
		add_shortcode( 'newsletter_block_form', array( &$this, 'deprecated_shortcode' ) );

		// add_filter( 'embed_html', array( &$this, 'embed_html' ), PHP_INT_MAX, 4 );
		add_filter( 'post_row_actions', array( &$this, 'quick_edit_btns' ), 10, 2 );

		$this->maybe_preview();
	}


	public function embed_html( $output, $post, $width, $height ) {

		// just skip if not on our cpt
		if ( 'mailster-form' != get_post_type() ) {
			return $settings;
		}

		// TODO: check if this works on forms
		$output = sprintf( '<iframe src="%s" sandbox="allow-scripts allow-forms" width="%d" height="%d"></iframe>', get_permalink( $post ), $width, $height );

		return $output;
	}


	public function get_theme_styles( $selector = '.is-root-container .mailster-block-form.wp-block-mailster-form-wrapper' ) {

		$value = '';

		$styles = wp_get_global_styles();

		if ( isset( $styles['color'] ) ) {
			$value .= $selector . '{';
			if ( isset( $styles['color']['background'] ) ) {
				$value .= 'background-color:' . preg_replace( '/^var:([a-z-]+)\|([a-z-]+)\|([a-z-]+)$/', 'var(--wp--$1--$2--$3)', $styles['color']['background'] ) . ';';
			}
			if ( isset( $styles['color']['text'] ) ) {
				$value .= 'color:' . preg_replace( '/^var:([a-z-]+)\|([a-z-]+)\|([a-z-]+)$/', 'var(--wp--$1--$2--$3)', $styles['color']['text'] ) . ';';
			}
			$value .= '}';
		}

		return $value;
	}


	public function disable_block_unlocks( $settings, $context ) {

		// just skip if not on our cpt
		if ( 'mailster-form' != get_post_type() || $context->name !== 'core/edit-post' ) {
			return $settings;
		}

		$settings['canLockBlocks'] = false;

		return $settings;
	}


	public function rest_api_init() {

		include MAILSTER_DIR . 'classes/rest-controller/rest.lists.class.php';

		$controller = new Mailster_REST_List_Controller();
		$controller->register_routes();

		include MAILSTER_DIR . 'classes/rest-controller/rest.form.class.php';

		$controller = new Mailster_REST_Form_Controller();
		$controller->register_routes();
	}

	public function register_settings() {

		register_setting(
			'mailster_settings',
			'mailster_inline_styles',
			array(
				'description'  => 'contains the styles of your sites input fields',
				'show_in_rest' => true,
				'type'         => 'string',
			)
		);
	}


	public function maybe_preview() {

		// use in setup wizard
		if ( isset( $_GET['_mailster_setup'] ) && wp_verify_nonce( $_GET['_mailster_setup'], 'mailster_setup' ) ) {
			add_filter( 'show_admin_bar', '__return_false' );
		}

		// enter preview mode
		if ( ! isset( $_GET['mailster-block-preview'] ) ) {
			return;
		}

		$data = json_decode( stripcslashes( $_GET['mailster-block-preview'] ), true );

		// stop if an error occurred
		if ( json_last_error() !== JSON_ERROR_NONE ) {
			return;
		}

		// sanitize
		$data = $this->sanitize( $data );

		if ( ! is_array( $data ) ) {
			return;
		}

		if ( ! isset( $data['p'] ) && 'other' != $data['type'] ) {
			$data['p'] = $this->get_preview_page( $data );
			// check if user can actually edit this form
			$redirect = add_query_arg(
				array(
					'mailster-block-preview' => rawurlencode( json_encode( $data ) ),
					'p'                      => $data['p'],
				),
				home_url()
			);

			wp_redirect( $redirect );
			exit;
		}

		if ( ! current_user_can( 'edit_post', $data['p'] ) ) {
			return;
		}

		// handle logged in status
		if ( ! $data['user'] ) {
			wp_set_current_user( 0 );
			add_filter( 'determine_current_user', '__return_false', PHP_INT_MAX );
		}

		$this->preview_data = $data;
	}


	public function sanitize( $value ) {

		if ( is_scalar( $value ) ) {
			$value = sanitize_text_field( $value );
		} elseif ( is_array( $value ) ) {
			$value = array_map( array( $this, 'sanitize' ), $value );
		} elseif ( is_object( $value ) ) {
			$vars = get_object_vars( $value );
			foreach ( $vars as $key => $data ) {
				$value->{$key} = $this->sanitize( $data );
			}
		}

		return $value;
	}


	public function get_preview_page( $data ) {

		global $wpdb;

		$sql = "SELECT posts.ID FROM {$wpdb->posts} AS posts";

		if ( ! empty( $data['options']['all'] ) ) {
			$sql .= ' WHERE posts.post_type IN ("' . implode( '", "', $data['options']['all'] ) . '")';
		} elseif ( ! empty( $data['options']['posts'] ) ) {
			$sql .= ' WHERE posts.ID IN (' . implode( ', ', $data['options']['posts'] ) . ')';
		} elseif ( ! empty( $data['options']['taxonomies'] ) ) {
			$sql .= " LEFT JOIN {$wpdb->term_relationships} AS terms ON terms.object_id = posts.ID WHERE terms.term_taxonomy_id IN (" . implode( ', ', $data['options']['taxonomies'] ) . ')';
		} else {
			$sql .= ' WHERE 1=1';
		}

		$sql .= " AND (posts.post_status = 'publish') ORDER BY posts.post_date DESC LIMIT 0, 1";

		return $wpdb->get_var( $sql );
	}


	public function wp_list_table_class_name( $class_name, $args ) {

		if ( $args['screen']->id !== 'edit-mailster-form' ) {
			return $class_name;
		}

		require_once MAILSTER_DIR . 'classes/block-forms.table.class.php';
		$class_name = 'Mailster_Block_Forms_Table';

		return $class_name;
	}


	public function columns( $columns ) {

		$columns = array(
			'cb'              => '<input type="checkbox" />',
			'title'           => esc_html__( 'Title', 'mailster' ),
			'block'           => esc_html__( 'Block / Shortcode', 'mailster' ),
			'info'            => '',
			'impressions'     => esc_html__( 'Impressions', 'mailster' ) . ' <span class="count">' . number_format_i18n( $this->get_impressions() ) . '</span>',
			'conversions'     => esc_html__( 'Conversions', 'mailster' ) . ' <span class="count">' . number_format_i18n( $this->get_conversions() ) . '</span>',
			'conversion_rate' => '<abbr title="' . esc_attr__( 'Conversation Rate', 'mailster' ) . '">' . esc_html__( 'CVR', 'mailster' ) . '</abbr> <span class="count">' . sprintf( '%s %%', number_format_i18n( $this->get_conversion_rate() * 100, 1 ) ) . '</span>',
			'date'            => esc_html__( 'Date', 'mailster' ),
		);
		return $columns;
	}


	public function custom_column( $column, $post_id ) {

		switch ( $column ) {
			case 'info':
				$placements = get_post_meta( $post_id, 'placements' );
				echo '<span title="' . esc_attr__( 'Displayed in Content', 'mailster' ) . '" class="form-option placement-content ' . ( in_array( 'content', $placements ) ? 'is-checked' : '' ) . '">&nbsp;</span>';
				echo '<span title="' . esc_attr__( 'Displayed as Popup', 'mailster' ) . '" class="form-option placement-popup ' . ( in_array( 'popup', $placements ) ? 'is-checked' : '' ) . '">&nbsp;</span>';
				echo '<span title="' . esc_attr__( 'Double-Opt-In', 'mailster' ) . '" class="form-option doubleoptin ' . ( get_post_meta( $post_id, 'doubleoptin', true ) ? 'is-checked' : '' ) . '">&nbsp;</span>';
				echo '<span title="' . esc_attr__( 'GDPR compliant', 'mailster' ) . '" class="form-option gdpr ' . ( get_post_meta( $post_id, 'gdpr', true ) ? 'is-checked' : '' ) . '">&nbsp;</span>';
				echo '<span title="' . esc_attr__( 'Overwrite user data', 'mailster' ) . '" class="form-option overwrite ' . ( get_post_meta( $post_id, 'overwrite', true ) ? 'is-checked' : '' ) . '">&nbsp;</span>';
				echo '<span title="' . sprintf( esc_attr__( 'Redirect users to %s after form submission.', 'mailster' ), get_post_meta( $post_id, 'redirect', true ) ) . '" class="form-option redirect ' . ( get_post_meta( $post_id, 'redirect', true ) ? 'is-checked' : '' ) . '">&nbsp;</span>';
				$lists = mailster( 'lists' )->get( get_post_meta( $post_id, 'lists', true ) );
				$names = wp_list_pluck( $lists, 'name', 'ID' );
				echo '<span title="' . sprintf( esc_attr__( 'Lists: %s', 'mailster' ), "\n" . implode( "\n", $names ) ) . '" class="form-option lists ' . ( count( $lists ) ? 'is-checked' : '' ) . '">' . count( $lists ) . '</span>';
				break;
			case 'block':
				esc_html_e( 'Copy', 'mailster' );
				echo ': ';
				echo '<a class="clipboard" title="' . esc_attr__( 'Click to copy block markup to your clipboard', 'mailster' ) . '" data-clipboard-text="' . esc_attr( '<!-- wp:mailster/form {"id":' . $post_id . '} /-->' ) . '">' . esc_html__( 'Block', 'mailster' ) . '</a> | ';
				echo '<a class="clipboard" title="' . esc_attr__( 'Click to copy shortcode to your clipboard', 'mailster' ) . '" data-clipboard-text="[mailster_form id=' . esc_attr( $post_id ) . ']">' . esc_html__( 'Shortcode', 'mailster' ) . '</a>';
				break;
			case 'impressions':
				echo number_format_i18n( $this->get_impressions( $post_id ) );
				break;
			case 'conversions':
				echo number_format_i18n( $this->get_conversions( $post_id ) );
				break;
			case 'conversion_rate':
				printf( '%s %%', number_format_i18n( $this->get_conversion_rate( $post_id ) * 100, 1 ) );
				break;
			default:
				break;
		}
	}




	public function prepare_forms() {

		if ( $this->preview_data ) {

			$options       = $this->preview_data['options'];
			$options['id'] = $this->preview_data['id'];

			$this->forms[ $this->preview_data['type'] ][] = $options;

			$suffix = SCRIPT_DEBUG ? '' : '.min';

			wp_enqueue_script( 'mailster-form-block-preview', MAILSTER_URI . 'assets/js/form-block-preview' . $suffix . '.js', array( 'jquery', 'mailster-form-view-script', 'wp-api-fetch' ), MAILSTER_VERSION );
			wp_enqueue_style( 'mailster-form-block-preview', MAILSTER_URI . 'assets/css/form-block-preview' . $suffix . '.css', array(), MAILSTER_VERSION );

		} elseif ( get_post_type() === 'mailster-form' ) {
			if ( ! is_post_publicly_viewable() ) {
				global $wp_query;
				$wp_query->set_404();
				status_header( 404 );
				nocache_headers();

			} else {

				// introduced in WordPress 7.0, to make InlineStyle work (access iframe document)
				// https://core.trac.wordpress.org/ticket/64740
				if ( function_exists( 'wp_set_up_cross_origin_isolation' ) && ! headers_sent() ) {
					header( 'Document-Isolation-Policy: isolate-and-credentialless' );
				}

				// TODO: check if there's a better way to alter the post content
				// $wp_query->post->post_content = $this->render_form_in_content( $wp_query->post->post_content );
				// add_filter( 'the_content', array( &$this, 'render_form_in_content' ) );
				// no output on the content
				add_filter( 'the_content', '__return_empty_string', PHP_INT_MAX );
				show_admin_bar( false );

				add_action( 'wp_body_open', array( &$this, 'render_form_in_body' ), PHP_INT_MIN );
				// this is a fallback for when wp_body_open is not used
				add_action( 'wp_footer', array( &$this, 'render_form_in_body' ), PHP_INT_MIN );
			}
		} elseif ( $forms = $this->query_forms() ) {
			$this->forms = $forms;
		}

		if ( isset( $this->forms['content'] ) ) {

			// TODO: check if there's a better way to alter the post content
			// $wp_query->post->post_content = $this->maybe_add_form_to_content( $wp_query->post->post_content );
			add_filter( 'the_content', array( &$this, 'maybe_add_form_to_content' ), PHP_INT_MAX );

		}
		if ( isset( $this->forms['popup'] ) || isset( $this->forms['bar'] ) || isset( $this->forms['side'] ) ) {

			// TODO: check if there's a better way to alter the post content
			// $wp_query->post->post_content = $this->maybe_add_form_to_content( $wp_query->post->post_content );
			add_action( 'the_content', array( &$this, 'maybe_add_popup_to_content' ), PHP_INT_MAX );

		}
	}


	public function maybe_add_form_for_button( string $content, array $block ) {

		if ( ! isset( $block['attrs']['mailster'] ) ) {
			return $content;
		}

		if ( ! isset( $this->forms['popup'] ) ) {
			$this->forms['popup'] = array();
		}

		$options = wp_parse_args(
			$block['attrs']['mailster'],
			array(
				'classes' => array( 'mailster-block-form-type-popup' ),
				'isPopup' => true,
			)
		);

		$form_html = $this->render_form( $options['id'], $options, false );

		$content .= "\n\n" . $form_html;

		return $content;
	}


	public function handle_embeds( string $content, array $block ) {

		$url     = $block['attrs']['url'];
		$encoded = str_replace( '&', '&amp;', $url );
		$oembed  = wp_oembed_get( $url );

		// find both, replace both
		$content = str_replace( $url, $oembed, $content );
		$content = str_replace( $encoded, $oembed, $content );

		return $content;
	}


	public function check_validity( $form_id, $options = array() ) {

		/**
		 * Pre-Filter the validity of the form. If false the form will not be displayed if true it will be displayed.
		 *
		 * @param bool $validity true if valid
		 * @param int $form_id the form id
		 * @param array $options the options
		 */
		$pre = apply_filters( 'mailster_block_form_pre_check_validity', null, $form_id, $options );
		if ( null !== $pre ) {
			return $pre;
		}

		// use this to skip the validity check
		if ( isset( $options['validity'] ) ) {
			return (bool) $options['validity'];
		}

		$post_type = get_post_type();

		// fails if not in a schedule (preview excluded)
		if ( ! $this->preview_data && ! empty( $options['schedule'] ) ) {
			$now  = current_time( 'timestamp' );
			$pass = false;

			foreach ( $options['schedule'] as $schedule ) {
				if ( $now > strtotime( $schedule['start'] ) && ( $now < strtotime( $schedule['end'] ) || empty( $schedule['end'] ) ) ) {
					$pass = true;
					break;
				}
			}
			if ( ! $pass ) {
				return false;
			}
		}

		$is_single_or_page = ( is_single() || is_page() );

		if ( ! empty( $options['all'] ) && in_array( $post_type, $options['all'] ) && $is_single_or_page ) {
			return true;
		}

		$current_id = get_the_ID();

		if ( isset( $options['posts'] ) && in_array( $current_id, $options['posts'] ) && $is_single_or_page ) {
			return true;
		}

		if ( ! empty( $options['taxonomies'] ) && $is_single_or_page ) {
			// get all assigned term ids of this post
			$terms = wp_get_object_terms( $current_id, get_object_taxonomies( $post_type ), array( 'fields' => 'ids' ) );
			if ( array_intersect( $options['taxonomies'], $terms ) ) {
				return true;
			}
		}

		return false;
	}

	public function deprecated_shortcode( $atts, $content ) {

		_deprecated_function( 'Shortcode \'[newsletter_block_form]\'', '4.0', '[mailster_form]' );

		return $this->shortcode( $atts, $content );
	}

	public function shortcode( $atts, $content ) {

		return $this->render_form( $atts['id'], array(), false );
	}

	public function render_form_in_body() {
		// if this is called from wp_body_open, remove the action from wp_footer
		if ( doing_action( 'wp_body_open' ) ) {
			remove_action( 'wp_footer', array( &$this, 'render_form_in_body' ), PHP_INT_MIN );
		}
		echo $this->render_form_in_content();
	}

	public function render_form_in_content( $content = '' ) {

		$id = get_the_ID();

		$options = array(
			'id'      => $id,
			'classes' => array( 'mailster-block-form-type-content' ),
		);

		return $this->render_form( $id, $options, false );
	}



	public function maybe_add_popup_to_content( $content ) {

		if ( ! is_singular() || is_feed() || is_preview() || is_admin() ) {
			return $content;
		}

		if ( ! isset( $this->forms['popup'] ) ) {
			return $content;
		}

		foreach ( $this->forms['popup'] as $options ) {

			if ( ! isset( $options['id'] ) ) {
				continue;
			}

			$options['classes'] = array( 'mailster-block-form-type-popup' );
			$options['isPopup'] = true;

			$form_html = $this->render_form( $options['id'], $options, true );

			$content .= "\n\n" . $form_html;
		}

		return $content;
	}

	public function maybe_add_form_to_content( $content ) {

		if ( ! is_singular() || is_feed() || is_preview() || is_admin() ) {
			return $content;
		}

		if ( ! isset( $this->forms['content'] ) ) {
			return $content;
		}

		foreach ( $this->forms['content'] as $options ) {
			$form_id = $options['id'];
			if ( isset( $displayed[ $form_id ] ) ) {
				continue;
			}

			// check this here so we don't hace to load the form
			if ( ! $this->check_validity( $form_id, $options ) ) {
				continue;
			}

			$display = $options['display'];

			// $options['id']      = $form_id;
			$options['classes'] = array( 'mailster-block-form-type-content' );

			$form_html = $this->render_form( $form_id, $options, false );

			if ( 'start' === $display ) {
				$content = $form_html . $content;
			} elseif ( 'end' === $display ) {
				$content = $content . "\n\n" . $form_html;

			} else {
				$split_at = $this->get_block_tag_by_tag( $options['tag'], has_blocks( $content ) );
				$pos      = (int) $options['pos'];

				if ( 'more' === $options['tag'] ) {
					$pos = 1;
				}

				$chunks = explode( $split_at, $content );
				if ( $pos < 0 ) {
					$pos = max( 0, count( $chunks ) + $pos );
				}

				if ( ! $pos && false === strpos( $content, $split_at ) ) {
					$pos = -1;
				}

				if ( isset( $chunks[ $pos ] ) ) {
					$chunks[ $pos ] = "\n\n" . $form_html . $chunks[ $pos ];
					$content        = implode( $split_at, $chunks );
				} else {
					$content .= "\n\n" . $form_html;
				}
			}
		}

		return $content;
	}

	private function get_block_tag_by_tag( $tag, $has_blocks = true ) {
		switch ( $tag ) {
			case 'p':
				return $has_blocks ? '<!-- /wp:paragraph -->' : '</p>';
			case 'more':
				return $has_blocks ? '<!-- /wp:more -->' : '<span id="more-' . get_the_ID() . '"></span>';
			case 'h2':
			case 'h3':
			case 'h4':
				return $has_blocks ? '<!-- /wp:heading -->' : '</' . $tag . '>';
		}

		return '</' . $tag . '>';
	}

	private function query_forms( $force = false ) {

		if ( $force || ! ( $forms = get_transient( 'mailster_forms' ) ) ) {

			$args = array(
				'post_type'     => 'mailster-form',
				'post_status'   => 'publish',
				'no_found_rows' => true,
				'fields'        => 'ids',
			);

			$query = new WP_Query( $args );

			$forms = array();

			foreach ( $query->posts as $form_id ) {
				$placements = (array) get_post_meta( $form_id, 'placements', false );
				// TODO check for A/B Test
				foreach ( $placements as $placement ) {
					$placement_options = get_post_meta( $form_id, 'placement_' . $placement, true );
					if ( $placement_options ) {
						$placement_options['id'] = $form_id;
						$forms[ $placement ][]   = $placement_options;
					}
				}
			}

			set_transient( 'mailster_forms', $forms );

		}

		return $forms;
	}


	public function register_post_type() {

		$name = mailster_option( 'legacy_forms' ) ? _x( 'Block Forms', 'Post Type General Name', 'mailster' ) : _x( 'Forms', 'Post Type General Name', 'mailster' );

		$labels = array(
			'name'                     => $name,
			'singular_name'            => _x( 'Form', 'Post Type Singular Name', 'mailster' ),
			'menu_name'                => $name,
			'attributes'               => __( 'Form Attributes', 'mailster' ),
			'all_items'                => $name,
			'add_new_item'             => __( 'Add New Form', 'mailster' ),
			'add_new'                  => __( 'Add New', 'mailster' ),
			'new_item'                 => __( 'New Form', 'mailster' ),
			'edit_item'                => __( 'Edit Form', 'mailster' ),
			'update_item'              => __( 'Update Form', 'mailster' ),
			'view_item'                => __( 'View Form', 'mailster' ),
			'view_items'               => __( 'View Forms', 'mailster' ),
			'search_items'             => __( 'Search Form', 'mailster' ),
			'not_found'                => __( 'Not found', 'mailster' ),
			'not_found_in_trash'       => __( 'Not found in Trash', 'mailster' ),
			'uploaded_to_this_item'    => __( 'Uploaded to this form', 'mailster' ),
			'items_list'               => __( 'Forms list', 'mailster' ),
			'items_list_navigation'    => __( 'Forms list navigation', 'mailster' ),
			'filter_items_list'        => __( 'Filter forms list', 'mailster' ),
			'item_published'           => __( 'Form published', 'mailster' ),
			'item_published_privately' => __( 'Form published privately.', 'mailster' ),
			'item_reverted_to_draft'   => __( 'Form reverted to draft.', 'mailster' ),
			'item_scheduled'           => __( 'Form scheduled.', 'mailster' ),
			'item_updated'             => __( 'Form updated.', 'mailster' ),

		);
		$args = array(
			'label'               => __( 'Form', 'mailster' ),
			'description'         => __( 'Newsletter Form', 'mailster' ),
			'labels'              => $labels,
			'supports'            => array( 'title', 'editor', 'revisions', 'custom-fields' ),
			'hierarchical'        => false,
			'public'              => false,
			'publicly_queryable'  => ! is_admin(),
			'capability_type'     => 'mailster-form',
			'show_ui'             => true,
			'show_in_menu'        => 'edit.php?post_type=newsletter',
			'show_in_admin_bar'   => false,
			'show_in_nav_menus'   => false,
			'can_export'          => false,
			'has_archive'         => false,
			'exclude_from_search' => true,
			'rewrite'             => array(
				'with_front' => false,
				'feeds'      => false,
				'slug'       => 'mailster-form',
			),
			'show_in_rest'        => true,

		);
		register_post_type( 'mailster-form', $args );
	}

	public function register_post_meta() {

		register_post_meta(
			'mailster-form',
			'doubleoptin',
			array(
				'type'         => 'boolean',
				'show_in_rest' => true,
				'single'       => true,
				'default'      => true,

			)
		);
		register_post_meta(
			'mailster-form',
			'gdpr',
			array(
				'type'         => 'boolean',
				'show_in_rest' => true,
				'single'       => true,
				'default'      => false,

			)
		);

		register_post_meta(
			'mailster-form',
			'userschoice',
			array(
				'type'         => 'boolean',
				'show_in_rest' => true,
				'single'       => true,
				'default'      => false,

			)
		);

		register_post_meta(
			'mailster-form',
			'redirect',
			array(
				'type'         => 'string',
				'show_in_rest' => true,
				'single'       => true,
				'default'      => '',

			)
		);

		register_post_meta(
			'mailster-form',
			'confirmredirect',
			array(
				'type'         => 'string',
				'show_in_rest' => true,
				'single'       => true,
				'default'      => '',

			)
		);

		register_post_meta(
			'mailster-form',
			'overwrite',
			array(
				'type'         => 'boolean',
				'show_in_rest' => true,
				'single'       => true,
				'default'      => true,

			)
		);

		register_post_meta(
			'mailster-form',
			'prefill',
			array(
				'type'         => 'boolean',
				'show_in_rest' => true,
				'single'       => true,
				'default'      => false,

			)
		);

		register_post_meta(
			'mailster-form',
			'lists',
			array(
				'type'         => 'array',
				'show_in_rest' => array(
					'schema' => array(
						'type'  => 'array',
						'items' => array(
							'type' => 'number',
						),
					),
				),
				'single'       => true,
				'default'      => array(),
			)
		);

		register_post_meta(
			'mailster-form',
			'subject',
			array(
				'type'         => 'string',
				'show_in_rest' => true,
				'single'       => true,
				'default'      => esc_html__( 'Welcome to {company}!', 'mailster' ),
			)
		);
		register_post_meta(
			'mailster-form',
			'headline',
			array(
				'type'         => 'string',
				'show_in_rest' => true,
				'single'       => true,
				'default'      => esc_html__( 'Please confirm your email', 'mailster' ),
			)
		);

		register_post_meta(
			'mailster-form',
			'content',
			array(
				'type'         => 'string',
				'show_in_rest' => true,
				'single'       => true,
				'default'      => sprintf( esc_html__( 'You have to confirm your email address to subscribe. Please click the link below to confirm. %s', 'mailster' ), "\n{link}" ),
			)
		);

		register_post_meta(
			'mailster-form',
			'link',
			array(
				'type'         => 'string',
				'show_in_rest' => true,
				'single'       => true,
				'default'      => esc_html__( 'Click here to confirm', 'mailster' ),
			)
		);

		register_post_meta(
			'mailster-form',
			'tags',
			array(
				'type'         => 'array',
				'show_in_rest' => array(
					'schema' => array(
						'type'  => 'array',
						'items' => array(
							'type' => 'number',
						),
					),
				),
				'single'       => true,
				'default'      => array(),
			)
		);

		register_post_meta(
			'mailster-form',
			'placements',
			array(
				'type'         => 'string',
				'show_in_rest' => true,
				'single'       => false,
			)
		);

		foreach ( array( 'content', 'bar', 'popup', 'side', 'other' ) as $placement_type ) {

			if ( 'content' == $placement_type ) {
				$default = array(
					'all'     => array( 'post' ),
					'tag'     => 'p',
					'pos'     => 0,
					'display' => 'end',
				);
			} elseif ( 'other' == $placement_type ) {
				$default = array();
			} else {
				$default = array(
					'all'              => array( 'post' ),
					'triggers'         => array( 'delay' ),
					'trigger_delay'    => 120,
					'trigger_inactive' => 120,
					'trigger_click'    => '',
					'trigger_scroll'   => 66,
					'width'            => 70,
					'cooldown'         => 24,

				);
			}

			register_post_meta(
				'mailster-form',
				'placement_' . $placement_type,
				array(
					'single'       => true,
					'type'         => 'object',
					'default'      => $default,
					'show_in_rest' => array(
						'schema' => array(
							'type'       => 'object',
							'properties' => array(
								'all'              => array(
									'type' => 'array',
								),
								'triggers'         => array(
									'type' => 'array',
								),
								'schedule'         => array(
									'type' => 'array',
								),
								'posts'            => array(
									'type' => 'array',
								),
								'taxonomies'       => array(
									'type' => 'array',
								),
								'tag'              => array(
									'type' => 'string',
								),
								'pos'              => array(
									'type' => 'integer',
								),
								'trigger_delay'    => array(
									'type' => 'integer',
								),
								'trigger_inactive' => array(
									'type' => 'integer',
								),
								'trigger_click'    => array(
									'type' => 'string',
								),
								'trigger_scroll'   => array(
									'type' => 'integer',
								),
								'display'          => array(
									'type' => 'string',
								),
								'align'            => array(
									'type' => 'string',
								),
								'width'            => array(
									'type' => 'integer',
								),
								'padding'          => array(
									'type'                 => 'object',
									'additionalProperties' => array(
										'type' => 'string',
									),
								),
								'animation'        => array(
									'type' => 'string',
								),
								'cooldown'         => array(
									'type' => 'integer',
								),
							),
						),
					),
				)
			);
		}
	}


	public function quick_edit_btns( $actions, $post ) {

		if ( get_post_type( $post ) != 'mailster-form' ) {
			return $actions;
		}

		if ( get_post_status( $post ) === 'publish' ) {
			$actions['form_preview'] = '<a href="' . get_permalink( $post->ID ) . '" data-title="' . esc_attr( sprintf( __( 'Preview %s', 'mailster' ), '"' . get_the_title( $post ) . '"' ) ) . '" aria-label="' . esc_attr__( 'Preview this form', 'mailster' ) . '">' . esc_html__( 'Preview', 'mailster' ) . '</a>';
		}

		// add ID to the very front
		$actions = array_merge( array( 'form_id' => sprintf( 'ID: %d', $post->ID ) ), $actions );

		return $actions;
	}



	public function get( $id ) {
		$post = get_post( $id );
		if ( 'mailster-form' !== $post->post_type ) {
			return false;
		}
		return $post;
	}


	public function get_all( $args = array() ) {

		$defaults = array(
			'post_type'      => 'mailster-form',
			'posts_per_page' => -1,
		);

		$args = wp_parse_args( $args, $defaults );

		return get_posts( $args );
	}

	public function get_list() {

		$forms = $this->get_all();

		$list = array();

		foreach ( $forms as $key => $value ) {
			$list[ $value->ID ] = $value->post_title;
		}

		return $list;
	}



	public function block_init() {

		register_block_type( MAILSTER_DIR . 'build/form', array( 'render_callback' => array( $this, 'render_form_callback' ) ) );
		register_block_type( MAILSTER_DIR . 'build/homepage/', array() );
		register_block_type( MAILSTER_DIR . 'build/homepage-context/', array( 'render_callback' => array( $this, 'render_homepage_context_callback' ) ) );

		if ( ! is_admin() ) {
			return;
		}

		global $pagenow, $post_id;
		$typenow = '';

		// from https://www.designbombs.com/registering-gutenberg-blocks-for-custom-post-type/
		if ( 'post-new.php' === $pagenow ) {
			if ( isset( $_REQUEST['post_type'] ) && post_type_exists( $_REQUEST['post_type'] ) ) {
				$typenow = sanitize_key( $_REQUEST['post_type'] );
			}
		} elseif ( 'post.php' === $pagenow ) {
			if ( isset( $_GET['post'] ) && isset( $_POST['post_ID'] ) && (int) $_GET['post'] !== (int) $_POST['post_ID'] ) {
				// Do nothing
			} elseif ( isset( $_GET['post'] ) ) {
				$post_id = (int) $_GET['post'];
			} elseif ( isset( $_POST['post_ID'] ) ) {
				$post_id = (int) $_POST['post_ID'];
			}
			if ( $post_id ) {
				if ( $post = get_post( $post_id ) ) {
					$typenow = $post->post_type;
				}
			}
			// in the site editor just bail out
		} elseif ( 'site-editor.php' === $pagenow ) {
			return;
		}

		// homepage only on pages
		if ( $typenow !== 'page' ) {
			// TODO check if unregistered above
			unregister_block_type( 'mailster/homepage' );
			unregister_block_type( 'mailster/homepage-context' );

		} else {
			$homepage = (int) mailster_option( 'homepage' );
			// on the newsletter homepage
			if ( $homepage && $post_id === $homepage ) {
				wp_add_inline_script( 'wp-blocks', 'var mailster_homepage_slugs = ' . json_encode( mailster_option( 'slugs' ) ) . ';' );
			} else {
				unregister_block_type( 'mailster/homepage' );
				unregister_block_type( 'mailster/homepage-context' );
			}
		}

		if ( $typenow === 'mailster-form' ) {

			// not in use on the form edit page
			unregister_block_type( 'mailster/form' );

			wp_enqueue_code_editor(
				array(
					'type'       => 'htmlmixed',
					'codemirror' => array( 'lint' => true ),
				)
			);

			$blocks = $this->get_blocks();

			foreach ( $blocks as $block ) {
				$args = array();

				$block_name = str_replace( '-', '_', basename( dirname( $block ) ) );

				if ( method_exists( $this, 'render_' . $block_name . '_callback' ) ) {
					$args['render_callback'] = array( $this, 'render_' . $block_name . '_callback' );
				}

				register_block_type( $block, $args );
			}
		}
	}

	private function get_blocks() {
		$blocks = glob( MAILSTER_DIR . 'build/forms/*/block.json' );
		return $blocks;
	}

	public function overview_script_styles() {

		$post_type = get_post_type();
		if ( ! $post_type ) {
			$post_type = get_current_screen()->post_type;
		}

		if ( 'mailster-form' != $post_type ) {
			return;
		}

		$suffix = SCRIPT_DEBUG ? '' : '.min';

		do_action( 'mailster_admin_header' );

		wp_enqueue_script( 'mailster-block-forms-overview', MAILSTER_URI . 'assets/js/block-form-overview-script' . $suffix . '.js', array( 'mailster-script', 'mailster-clipboard-script' ), MAILSTER_VERSION, true );
		wp_enqueue_style( 'mailster-block-forms-overview', MAILSTER_URI . 'assets/css/block-form-overview' . $suffix . '.css', array(), MAILSTER_VERSION );

		wp_enqueue_style( 'thickbox' );
		wp_enqueue_script( 'thickbox' );
	}

	public function block_script_styles() {

		if ( 'mailster-form' != get_post_type() ) {
			return;
		}

		$suffix = SCRIPT_DEBUG ? '' : '.min';

		do_action( 'mailster_admin_header' );

		wp_enqueue_style( 'mailster-form-block-editor', MAILSTER_URI . 'assets/css/forms-blocks-editor' . $suffix . '.css', array(), MAILSTER_VERSION );
		wp_add_inline_style( 'mailster-form-block-editor', $this->get_theme_styles() );
		wp_add_inline_script( 'wp-blocks', 'var mailster_fields = ' . json_encode( array_values( $this->get_fields() ) ) . ';' );
	}

	public function get_fields() {
		$custom_fields = mailster()->get_custom_fields();

		$fields = array(
			'submit'    => array(
				'name'    => __( 'Submit Button', 'mailster' ),
				'id'      => 'submit',
				'default' => mailster_text( 'submitbutton' ),
				'type'    => 'submit',
			),
			'email'     => array(
				'name' => mailster_text( 'email' ),
				'id'   => 'email',
				'type' => 'email',
			),
			'firstname' => array(
				'name' => mailster_text( 'firstname' ),
				'id'   => 'firstname',
				'type' => 'text',
			),

			'lastname'  => array(
				'name' => mailster_text( 'lastname' ),
				'id'   => 'lastname',
				'type' => 'text',
			),
		);

		return array_merge( $fields, $custom_fields );
	}

	public function allowed_block_types( $allowed_block_types, $context ) {

		// just skip if not on our cpt
		if ( 'mailster-form' != get_post_type() || $context->name !== 'core/edit-post' ) {
			return $allowed_block_types;
		}

		$block_types = WP_Block_Type_Registry::get_instance()->get_all_registered();
		$types       = array_keys( $block_types );

		// only allow core and Mailster blocks
		$types = preg_grep( '/^(core|mailster)\//', $types );

		// remove certain blocks
		$remove = array( 'core/more', 'core/read-more', 'core/nextpage' );
		$remove = array_merge( $remove, preg_grep( '/core\/(comment-|comments-|navigation-|post-|query-)/', $types ) );
		$types  = array_diff( $types, $remove );

		$custom_fields = array_keys( $this->get_fields() );
		foreach ( $custom_fields as $block ) {
			$types[] = 'mailster/field-' . str_replace( '_', '-', $block );
		}
		return apply_filters( 'mailster_forms_allowed_block_types', array_values( $types ) );
	}

	public function block_editor_settings( $editor_settings, $block_editor_context ) {

		if ( get_post_type( $block_editor_context->post ) !== 'mailster-form' ) {
			return $editor_settings;
		}

		// add inline styles
		if ( $css = get_option( 'mailster_inline_styles' ) ) {
			$editor_settings['styles'][] = array(
				'css'            => $css,
				'__unstableType' => 'user',
				'isGlobalStyles' => false,
			);
		}

		// disable code editor
		// $editor_settings['codeEditingEnabled'] = false;

		return $editor_settings;
	}

	public function force_block_editor( $bool, $post_type ) {

		// just pass through
		if ( $post_type !== 'mailster-form' ) {
			return $bool;
		}

		return true;
	}

	public function block_categories( $categories ) {

		if ( 'mailster-form' != get_post_type() ) {
			return $categories;
		}

		return array_merge(
			array(
				array(
					'slug'  => 'mailster-form-fields',
					'title' => __( 'Newsletter Form Fields', 'mailster' ),
				),
			),
			$categories
		);
	}

	public function register_block_patterns() {

		register_block_pattern_category( 'mailster-forms', array( 'label' => __( 'Mailster Forms', 'mailster' ) ) );

		include_once MAILSTER_DIR . 'patterns/forms.php';
	}


	public function render_form( $form, $options = array(), $check_validity = true ) {

		$form = get_post( $form );

		if ( ! $form ) {
			return '';
		}

		if ( get_post_type( $form ) != 'mailster-form' ) {
			return '';
		}

		if ( $check_validity && ! $this->check_validity( $form->ID, $options ) ) {
			return '';
		}

		$options['id'] = $form->ID;

		if ( isset( $this->preview_data ) && $form->post_status != 'auto-draft' ) {
			$options['classes'][] = 'wp-block-mailster-form-outside-wrapper-placeholder';
		}

		$form_block = '<!-- wp:mailster/form ' . json_encode( $options ) . ' /-->';

		return do_blocks( $form_block );
	}


	public function kses( $content ) {

		// allow form elements in KSES
		add_filter( 'wp_kses_allowed_html', array( $this, 'wp_kses_allowed_html' ) );
		$content = wp_kses_post( $content );
		// only here
		remove_filter( 'wp_kses_allowed_html', array( $this, 'wp_kses_allowed_html' ) );

		return $content;
	}


	public function wp_kses_allowed_html( $tags ) {

		$tags['a']['tabindex']     = true;
		$tags['a']['aria-role']    = true;
		$tags['div']['tabindex']   = true;
		$tags['div']['aria-modal'] = true;
		$tags['div']['hidden']     = true;
		$tags['dialog']            = array(
			'id'            => true,
			'class'         => true,
			'name'          => true,
			'type'          => true,
			'value'         => true,
			'spellcheck'    => true,
			'autocomplete'  => true,
			'aria-required' => true,
			'aria-label'    => true,
			'required'      => true,
			'placeholder'   => true,
			'checked'       => true,
		);
		$tags['input']             = array(
			'id'            => true,
			'class'         => true,
			'style'         => true,
			'name'          => true,
			'type'          => true,
			'value'         => true,
			'spellcheck'    => true,
			'autocomplete'  => true,
			'aria-required' => true,
			'aria-label'    => true,
			'required'      => true,
			'placeholder'   => true,
			'checked'       => true,
		);
		$tags['select']            = $tags['input'];
		$tags['form']              = array(
			'action'     => true,
			'method'     => true,
			'novalidate' => true,
			'class'      => true,
			'style'      => true,
			'data-*'     => true,
			'aria-*'     => true,
		);
		$tags['style']             = array(
			'class' => true,
		);
		$tags['script']            = array(
			'class' => true,
			'type'  => true,
		);
		$tags['option']            = array(
			'value' => true,
		);
		$tags['svg']               = array(
			'viewbox' => true,
		);
		$tags['path']              = array(
			'd' => true,
		);

		return $tags;
	}


	public function get_identifier( $form, $args = array() ) {

		$form = get_post( $form );

		$uniqid = get_post_meta( $form->ID, 'uniqid', true );
		if ( ! $uniqid ) {
			$uniqid = uniqid();
			update_post_meta( $form->ID, 'uniqid', $uniqid );
		}

		$identifier_args = array(
			'id'     => $form->ID,
			'uniqid' => $uniqid,
			'args'   => $args,
		);

		// create identifier based on arguments
		return hash( 'crc32', md5( serialize( $identifier_args ) ) );
	}


	public function render_homepage_context_callback( $args, $content, WP_Block $block ) {

		$type = isset( $block->parsed_block['attrs']['type'] ) ? $block->parsed_block['attrs']['type'] : 'submission';

		$mailster_page = get_query_var( '_mailster_page', 'submission' );

		// do not render unsubscribe or profile on the wrong page
		if ( $mailster_page && $mailster_page !== $type ) {
			return;
		}

		return $content;
	}





	public function render_form_callback( $args, $content, WP_Block $block ) {

		// maybe we are in a context (homepage)
		$block_context = 'submission';
		if ( ! isset( $args['id'] ) ) {
			$block_context = isset( $block->context['mailster-homepage-context/type'] ) ? $block->context['mailster-homepage-context/type'] : null;
			if ( ! $block_context ) {
				return;
			}
			$args['id'] = isset( $block->context[ 'mailster-homepage-context/' . $block_context ] ) ? $block->context[ 'mailster-homepage-context/' . $block_context ] : null;

			if ( ! $args['id'] ) {
				return;
			}
		}

		$form = get_post( $args['id'] );

		if ( ! $form ) {
			return;
		}

		// further checks for revisions
		if ( get_post_type( $form ) === 'revision' ) {
			$form = get_post( $form->post_parent );
			if ( get_post_type( $form ) != 'mailster-form' ) {
				return;
			}
		}

		if ( isset( $args['identifier'] ) ) {
			$identifier = $args['identifier'];
		} else {
			// create identifier based on arguments
			$identifier = $this->get_identifier( $form, $args );
		}

		// is on a page in the backend and loaded via the REST API
		$is_backend = defined( 'REST_REQUEST' ) && REST_REQUEST;

		// get context of the form
		$type = isset( $args['type'] ) ? $args['type'] : $block_context;

		$mailster_page = get_query_var( '_mailster_page', 'submission' );

		// do not render unsubscribe or profile on the wrong page
		if ( ! $is_backend && $mailster_page && $mailster_page !== $type ) {
			return;
		}

		wp_enqueue_script( 'mailster-form-view-script' );

		$args = wp_parse_args(
			$args,
			array(
				'identifier' => $identifier,
				'classes'    => array( 'mailster-block-form-type-content' ), // gets overwritten by other types
				'cooldown'   => 0,
				'isPreview'  => false,
				'isPopup'    => false,
				'type'       => $block_context,
				'prefill'    => (bool) get_post_meta( $form->ID, 'prefill', true ),

			)
		);

		$content = $form->post_content;

		// is on a page in the backend and loaded via the REST API
		$is_preview = false;
		$is_popup   = $args['isPopup'];

		// in preview mode check for content here
		if ( $is_backend ) {

			$request_body = file_get_contents( 'php://input' );
			if ( ! empty( $request_body ) ) {
				$data = json_decode( $request_body, true );

				if ( isset( $data['block_form_content'] ) ) {
					$content = $data['block_form_content'];
				}
				if ( isset( $data['args'] ) ) {
					// merge sent attributes with block attributes
					$args = wp_parse_args( $data['args'], $args );
				}

				$is_preview = true;

			}
		}

		// replaces content for unsubscribe page or profile page
		$content    = $this->prepare_for_type( $content, $type );
		$form_block = $this->get_form_block_from_content( $content );

		// embeds do not work this way: https://github.com/WordPress/gutenberg/issues/15471
		// this works for FSE themes but sometimes not for mixed and legacy themes
		add_filter( 'render_block_core/embed', array( &$this, 'handle_embeds' ), 10, 2 );
		$output = render_block( $form_block );
		remove_filter( 'render_block_core/embed', array( &$this, 'handle_embeds' ), 10, 2 );

		$args['classes'][] = 'wp-block-mailster-form-outside-wrapper';
		$args['classes'][] = 'wp-block-mailster-form-outside-wrapper-' . $identifier;
		$args['classes'][] = 'wp-block-mailster-form-outside-wrapper-' . $form->ID;

		// get block gap. May get overwritten by embedding form.
		$block_gap = null;

		if ( isset( $block->attributes['align'] ) ) {
			$args['classes'][] = 'align' . $block->attributes['align'];
		}

		if ( isset( $form_block['attrs']['className'] ) ) {
			$args['classes'][] = $form_block['attrs']['className'];
		}

		$custom_styles = array();

		if ( isset( $form_block['attrs']['padding'] ) ) {
			$custom_styles[''][] = 'padding:' . (int) $form_block['attrs']['padding'] . 'px';
		}
		if ( isset( $form_block['attrs']['color'] ) ) {
			$custom_styles[''][] = 'color:' . $form_block['attrs']['color'];
		}
		if ( isset( $args['width'] ) ) {
			$custom_styles['.mailster-block-form'][] = 'flex-basis:' . (int) $args['width'] . '%';
		}
		if ( isset( $args['padding'] ) ) {
			foreach ( $args['padding'] as $key => $value ) {
				$custom_styles['.mailster-block-form'][] = 'padding-' . $key . ':' . $value;
			}
		}
		if ( isset( $args['animation'] ) ) {
			$args['classes'][] = 'has-animation animation-' . $args['animation'];
		}

		if ( isset( $form_block['attrs']['background']['image'] ) ) {

			$background = $form_block['attrs']['background'];

			$custom_styles['::before'] = array(
				'content:"";top:0;left:0;bottom:0;right:0;',
				'background-image:url(' . $background['image'] . ')',
				'opacity:' . $background['opacity'] . '%',
			);
			if ( $background['fixed'] ) {
				$custom_styles['::before'][] = 'background-attachment:fixed';
			}
			if ( isset( $background['fullscreen'] ) && $background['fullscreen'] ) {
				$args['classes'][]           = 'mailster-form-is-fullscreen';
				$custom_styles['::before'][] = 'position:fixed';
				$custom_styles['::before'][] = 'background-size:cover';
			} else {
				$custom_styles['::before'][] = 'position:absolute';
				$custom_styles['::before'][] = 'background-size:' . ( ! is_numeric( $background['size'] ) ? $background['size'] : $background['size'] . '%' );
				$custom_styles['::before'][] = 'background-position:' . ( $background['position']['x'] * 200 - 50 ) . '% ' . ( $background['position']['y'] * 100 ) . '%';
			}
			if ( $background['repeat'] ) {
				$custom_styles['::before'][] = 'background-repeat:repeat';
			} else {
				$custom_styles['::before'][] = 'background-repeat:no-repeat';
			}
		}
		if ( isset( $form_block['attrs']['borderRadius'] ) ) {
			$custom_styles[''][]         = 'border-radius:' . $form_block['attrs']['borderRadius'];
			$custom_styles['::before'][] = 'border-radius:' . $form_block['attrs']['borderRadius'];
		}

		if ( isset( $form_block['attrs']['style'] ) ) {
			$custom_styles[' .mailster-label'] = array();
			$custom_styles[' .input']          = array();

			foreach ( $form_block['attrs']['style'] as $key => $value ) {
				if ( $value ) {
					switch ( $key ) {
						case 'labelColor':
							$custom_styles[' .mailster-label'][] = 'color:' . $value;
							break;
						case 'inputColor':
							$key = 'color';
						case 'borderWidth':
						case 'backgroundColor':
						case 'borderColor':
						case 'borderRadius':
							$custom_styles[' .input'][] = strtolower( preg_replace( '/([a-z])([A-Z])/', '$1-$2', $key ) ) . ':' . $value;
							break;
						case 'spacing':
							// overwrite
							if ( isset( $value['blockGap'] ) ) {
								$block_gap = $value['blockGap'];
							}
							break;
					}
				}
			}
		}

		$embeded_style = '';

		// overwrite block gap from embedding form. The block gap from the form block is not used here.
		if ( isset( $args['style']['spacing'] ) && isset( $args['style']['spacing']['blockGap'] ) ) {
			$block_gap = $args['style']['spacing']['blockGap'];
		}

		// handle block gap if set
		if ( $block_gap ) {
			$layout_css     = wp_get_layout_style(
				'.wp-block-mailster-form-outside-wrapper-' . $identifier . ' .mailster-block-form-inner',
				null,
				true,        // $has_block_gap_support (theme supports it)
				$block_gap,  // gap from style.spacing.blockGap
			);
			$embeded_style .= $layout_css;
		}

		foreach ( $custom_styles as $selector => $property ) {
			$embeded_style .= '.wp-block-mailster-form-outside-wrapper-' . $identifier . ' .wp-block-mailster-form-wrapper' . $selector . '{';
			$embeded_style .= strip_tags( implode( ';', $property ) );
			$embeded_style .= '}';
		}

		// add theme specific styles
		if ( $is_popup ) {
			$embeded_style .= $this->get_theme_styles( '.wp-block-mailster-form-outside-wrapper-' . $identifier . ':not(.mailster-block-form-type-content) .wp-block-mailster-form-wrapper.mailster-block-form' );
		}

		// add inline styles from block for visual accuracy (only backend)
		if ( ! $is_preview && $is_backend && $input_styles = get_option( 'mailster_inline_styles' ) ) {
			$embeded_style .= $input_styles;
		}

		// embeding events
		if ( isset( $form_block['attrs']['events'] ) ) {
			$events = '';
			foreach ( $form_block['attrs']['events'] as $eventname => $rawjs ) {
				if ( empty( $rawjs ) ) {
					continue;
				}

				$events .= 'window.mailsterBlockEvents[' . $form->ID . ']["' . esc_attr( $eventname ) . '"] = function(){' . $rawjs . '};';
			}
			if ( $events ) {
				$output = '<script class="mailster-form-script-' . esc_attr( $identifier ) . '">window.mailsterBlockEvents = window.mailsterBlockEvents || {};window.mailsterBlockEvents[' . esc_attr( $form->ID ) . '] = window.mailsterBlockEvents[' . esc_attr( $form->ID ) . '] || {};' . $events . '</script>' . $output;
			}
		}

		if ( isset( $form_block['attrs']['css'] ) ) {

			$htmldoc = new \InlineStyle\InlineStyle();

			foreach ( $form_block['attrs']['css'] as $name => $rawcss ) {
				if ( empty( $rawcss ) ) {
					continue;
				}

				// just css
				$rawcss = wp_strip_all_tags( $rawcss );

				$parsed    = $htmldoc->parseStylesheet( $rawcss );
				$css       = '';
				$css_rules = array();
				foreach ( $parsed as $rule ) {
					$selector = array_shift( $rule );
					if ( ! empty( $rule ) ) {
						// wrapper needs no extra space
						if ( '.wp-block-mailster-form-outside-wrapper' != $selector ) {
							$selector = ' ' . $selector;
						}

						// prefix for more specificity
						$selector = 'div.wp-block-mailster-form-outside-wrapper.wp-block-mailster-form-outside-wrapper-' . esc_attr( $identifier ) . '.wp-block-mailster-form-outside-wrapper-' . esc_attr( $form->ID ) . $selector;

						$rule = implode( ';', $rule );

						// check if we have same values for different selectors and merge them
						if ( false !== ( $key = array_search( $rule, $css_rules ) ) ) {
							$css_rules[ $key . ',' . $selector ] = $rule;
							unset( $css_rules[ $key ] );
						} else {
							$css_rules[ $selector ] = $rule;
						}
					}
				}

				foreach ( $css_rules as $selectors => $value ) {
					$css .= $selectors . '{' . $value . '}';
				}

				switch ( $name ) {
					case 'tablet':
						$embeded_style .= '@media only screen and (max-width: 800px) {' . $css . '}';
						break;
					case 'mobile':
						$embeded_style .= '@media only screen and (max-width: 400px) {' . $css . '}';
						break;
					default:
						$embeded_style .= $css;
						break;
				}
			}

			$embeded_style = str_replace( array( "\r", "\n", "\t" ), '', $embeded_style );
		}

		if ( ! empty( $embeded_style ) ) {
			$output = '<style class="mailster-form-style-' . esc_attr( $identifier ) . '">' . wp_strip_all_tags( $embeded_style ) . '</style>' . $output;
		}

		$extra_attributes = array( 'class' => implode( ' ', $args['classes'] ) );

		if ( $is_popup ) {
			$extra_attributes['aria-modal']  = 'true';
			$extra_attributes['aria-label']  = esc_attr__( 'Newsletter Signup Form', 'mailster' );
			$extra_attributes['role']        = 'div';
			$extra_attributes['aria-hidden'] = 'true';
			$extra_attributes['tabindex']    = '-1';
			$extra_attributes['hidden']      = 'true';
		}

		$wrapper_attributes = get_block_wrapper_attributes( $extra_attributes );

		// add wrapper attributes
		$output = sprintf(
			'<div %s>%s</div>',
			$wrapper_attributes,
			$output
		);

		// replace shortcodes after caching
		$output = do_shortcode( $output );

		$form_args = array(
			'id'         => $args['id'],
			'identifier' => $args['identifier'],
			'cooldown'   => $args['cooldown'],
			'isPreview'  => $args['isPreview'],
			'type'       => $args['type'],
			'prefill'    => $args['prefill'],
		);

		if ( isset( $args['triggers'] ) ) {
			$form_args['triggers'] = $args['triggers'];
			foreach ( $args['triggers'] as $trigger ) {
				if ( isset( $args[ 'trigger_' . $trigger ] ) ) {
					$form_args[ 'trigger_' . $trigger ] = $args[ 'trigger_' . $trigger ];
				}
			}
		}

		$inject  = '';
		$inject .= '<script class="mailster-block-form-data" type="application/json">' . json_encode( $form_args ) . '</script>';
		$inject .= '<input name="_timestamp" type="hidden" value="' . esc_attr( time() ) . '" />' . "\n";
		if ( $type !== 'submission' ) {
			$inject .= '<input name="_hash" type="hidden" value="" />' . "\n";
		}
		$campaign_id = get_query_var( '_mailster_extra' );
		if ( $campaign_id ) {
			$inject .= '<input name="_campaign_id" type="hidden" value="' . esc_attr( $campaign_id ) . '" />' . "\n";
		}

		$inject .= '<button class="mailster-block-form-close" aria-label="' . esc_attr__( 'close', 'mailster' ) . '" tabindex="0"><svg viewbox="0 0 100 100"><path d="M100 10.71 89.29 0 50 39.29 10.71 0 0 10.71 39.29 50 0 89.29 10.71 100 50 60.71 89.29 100 100 89.29 60.71 50z"/></svg></button>';

		// add honeypot field
		if ( ! $is_backend && apply_filters( 'mailster_honeypot', mailster_option( 'check_honeypot' ), $form->ID ) ) {
			$inject .= '<div style="position:absolute;top:-99999px;' . ( is_rtl() ? 'right' : 'left' ) . ':-99999px;z-index:-99;"><input name="_n_hp_mail" type="text" tabindex="-1" autocomplete="noton" autofill="off" aria-hidden="true"></div>';
		}
		$output = str_replace( '</form>', $inject . '</form>', $output );

		// run kses thourgh html blocks
		// TODO: check the output on forms with custom padding/margin
		// $output = $this->kses( $output );

		return apply_filters( 'mailster_block_form', $output, $form->ID, $form_args );
	}

	public function get_required_fields( $form ) {

		if ( ! ( $form = get_post( $form ) ) ) {
			return;
		}

		// get all input, select or textareas fields which have an required attribute
		if ( preg_match_all( '/<(input|select|textarea)(.*?)name="(.*?)"(.*?)(aria-required="true")(.*?)>/', $form->post_content, $matches, PREG_SET_ORDER, 0 ) ) {

			$fields = array_values( array_unique( wp_list_pluck( $matches, 3 ) ) );

			return $fields;

		}

		return array();
	}

	/**
	 * Prepare the content for Unsubscribe and Profile pages
	 *
	 * @param string $content
	 * @param array  $type
	 *
	 * @return string
	 */
	private function prepare_for_type( $content, $type ) {

		$submit_button = null;

		// on profile and unsubscribe remove certain fields
		if ( $type === 'unsubscribe' ) {

			// remove custom fields
			$fields   = mailster()->get_custom_fields( true );
			$fields[] = 'firstname';
			$fields[] = 'lastname';
			$fields   = implode( '|', $fields );
			// remove all custom fields
			$content = preg_replace( '/<!-- wp:(mailster\/field\-(' . $fields . '))(.*?)-->(.*?)<!-- \/wp:(\1) -->/s', '', $content );

			// remove gdpr and lists
			$content = preg_replace( '/<!-- wp:(mailster\/(gdpr|lists))(.*?)-->(.*?)<!-- \/wp:(\1) -->/s', '', $content );

			$submit_button = mailster_text( 'unsubscribebutton' );

		} elseif ( $type === 'profile' ) {

			// remove gdpr
			$content = preg_replace( '/<!-- wp:(mailster\/(gdpr))(.*?)-->(.*?)<!-- \/wp:(\1) -->/s', '', $content );

			$dropdown = '<div class="wp-block-mailster-field-status mailster-wrapper mailster-wrapper-type-status"><label for="mailster-input-status" class="mailster-label">Status</label><select name="_status" id="mailster-input-status" class="input" aria-required="false" aria-label="Dropdown"><option value="1">' . esc_html__( 'Subscribed', 'mailster' ) . '</option><option value="2">' . esc_html__( 'Unsubscribed', 'mailster' ) . '</option></select></div>';

			$dropdown = '';

			$content = preg_replace( '/(<!-- wp:(mailster\/(field\-email))(.*?)-->(.*?)<!-- \/wp:(\2) -->)/s', $dropdown . ' $1', $content );

			$submit_button = mailster_text( 'profilebutton' );

		}

		if ( $submit_button ) {
			$content = preg_replace( '/<!-- wp:(mailster\/field\-submit)(.*?)-->(.*?)(value=")(.*?)(")(.*?)<!-- \/wp:(\1) -->/s', '<!-- wp:mailster/field-submit$2-->$3$4' . $submit_button . '$6$7<!-- /wp:mailster/field-submit-->', $content );
		}
		return $content;
	}



	private function get_form_block( $form ) {

		$form = get_post( $form );
		if ( ! $form ) {
			return null;
		}
		return $this->get_form_block_from_content( $form->post_content );
	}


	private function get_form_block_from_content( $post_content ) {

		$parsed = parse_blocks( $post_content );
		foreach ( $parsed as $block ) {
			if ( $block['blockName'] == 'mailster/form-wrapper' ) {
				return $block;
			}
		}

		return null;
	}

	public function maybe_set_homepage( $post_id, $post, $update ) {

		$homepage = mailster_option( 'homepage' );

		if ( preg_match( '/<!-- wp:(mailster\/(homepage))(.*?)-->(.*?)<!-- \/wp:(\1) -->/s', $post->post_content, $matches ) ) {

			if ( ! $homepage ) {
				mailster_update_option( 'homepage', $post_id );
			}
		}

		if ( ! $homepage ) {

		}
	}

	public function clear_cache( $post_id ) {

		update_post_meta( $post_id, '_cache', array() );
		update_post_meta( $post_id, '_cache_time', array() );
		delete_transient( 'mailster_forms' );
	}

	public function clear_inline_style() {

		update_option( 'mailster_inline_styles', '', 'no' );
	}


	public function impression( $form_id, $subscriber_id = null, $post_id = null ) {

		$current_user = get_current_user_id();
		if ( $current_user && $current_user == get_post( $form_id )->post_author ) {
			return new WP_Error( 'no_action', 'no action for author of the form', array( 'status' => 406 ) );
		}
		return $this->action( 'impression', $form_id, $subscriber_id, $post_id );
	}

	public function signup( $form_id, $subscriber_id = null, $post_id = null ) {

		return $this->action( 'signup', $form_id, $subscriber_id, $post_id );
	}

	public function conversion( $form_id, $subscriber_id = null, $post_id = null ) {

		return $this->action( 'conversion', $form_id, $subscriber_id, $post_id );
	}


	private function action( $type, $form_id, $subscriber_id, $post_id ) {

		global $wpdb;

		$actions = array(
			'impression' => 1,
			'signup'     => 2,
			'conversion' => 3,
		);

		if ( ! isset( $actions[ $type ] ) ) {
			return new WP_Error( 'invalid_action', 'There is no such action' );
		}

		$action_type = $actions[ $type ];

		$data = array(
			'timestamp' => time(),
			'type'      => $action_type,
		);

		// always create a new entry on impression
		if ( $action_type == 1 ) {
			$entry = false;
		} else {
			$entry = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}mailster_form_actions WHERE form_id = %d AND subscriber_id = %d AND subscriber_id != 0", $form_id, $subscriber_id ) );
		}

		// entry exists
		if ( $entry ) {

			if ( false !== $wpdb->update( "{$wpdb->prefix}mailster_form_actions", $data, array( 'ID' => $entry->ID ) ) ) {
				do_action( 'mailster_form_' . $type, $form_id, $subscriber_id, $entry->post_id );
			}

			return true;
		}

		$data = wp_parse_args(
			$data,
			array(
				'form_id'       => $form_id,
				'post_id'       => $post_id,
				'subscriber_id' => $subscriber_id,
			)
		);

		if ( false !== $wpdb->insert( "{$wpdb->prefix}mailster_form_actions", $data ) ) {
			do_action( 'mailster_form_' . $type, $form_id, $subscriber_id, $post_id );
		}

		return true;
	}

	public function get_impressions( $form_id = '' ) {

		global $wpdb;

		$cache_key = 'form_get_impressions_' . $form_id;

		if ( false === ( $val = mailster_cache_get( $cache_key ) ) ) {

			$sql = "SELECT COUNT(*) FROM {$wpdb->prefix}mailster_form_actions WHERE type = 1";
			if ( $form_id ) {
				$sql .= $wpdb->prepare( ' AND form_id = %d', $form_id );
			}
			$val = $wpdb->get_var( $sql );

			mailster_cache_add( $cache_key, $val );
		}

		return (int) $val;
	}

	public function get_conversions( $form_id = '' ) {

		global $wpdb;
		$cache_key = 'form_get_conversions_' . $form_id;

		if ( false === ( $val = mailster_cache_get( $cache_key ) ) ) {

			$sql = "SELECT COUNT(*) FROM {$wpdb->prefix}mailster_subscriber_meta AS meta LEFT JOIN {$wpdb->prefix}mailster_subscribers AS subscribers ON subscribers.ID = meta.subscriber_id WHERE subscribers.status != 0";
			if ( $form_id ) {
				$sql .= $wpdb->prepare( " AND meta.meta_key = 'form' AND  meta.meta_value = %d", $form_id );
			}
			$val = $wpdb->get_var( $sql );

			$sql = "SELECT COUNT(*) FROM {$wpdb->prefix}mailster_form_actions WHERE type = 3";
			if ( $form_id ) {
				$sql .= $wpdb->prepare( ' AND form_id = %d', $form_id );
			}
			$val = $wpdb->get_var( $sql );

			mailster_cache_add( $cache_key, $val );
		}

		return (int) $val;
	}

	public function get_conversion_rate( $form_id = '' ) {

		$impressions = $this->get_impressions( $form_id );
		$conversions = $this->get_conversions( $form_id );

		$rate = $impressions ? $conversions / $impressions : 0;

		return max( min( 1, $rate ), 0 );
	}


	public function on_install( $new = false ) {

		if ( $new ) {

			$content = require MAILSTER_DIR . 'patterns/default-form.php';

			wp_insert_post(
				array(
					'post_title'   => esc_html__( 'Default Form', 'mailster' ),
					'post_content' => wp_slash( $content ),
					'post_status'  => 'publish',
					'post_type'    => 'mailster-form',
				)
			);

			update_option( 'mailster_inline_styles', '', 'no' );

		}
	}
}
