<?php
defined( 'ABSPATH' ) || exit;

/**
 * Jalpi_Forms — native form plugin integrations for Jalpi Page Builder.
 *
 * Detects active plugins, serves REST endpoints so the editor can list
 * available forms, and renders form HTML using each plugin's own API.
 *
 * Supported: Contact Form 7, Forminator, WPForms (Lite & Pro), Gravity Forms.
 */
class Jalpi_Forms {

	private static ?self $instance = null;

	/** Slug → plugin file(s). Arrays = "any of these is sufficient". */
	const PLUGINS = [
		'cf7'          => 'contact-form-7/wp-contact-form-7.php',
		'forminator'   => 'forminator/forminator.php',
		'wpforms'      => [ 'wpforms-lite/wpforms.php', 'wpforms/wpforms.php' ],
		'gravityforms' => 'gravityforms/gravityforms.php',
	];

	const PLUGIN_LABELS = [
		'cf7'          => 'Contact Form 7',
		'forminator'   => 'Forminator',
		'wpforms'      => 'WPForms',
		'gravityforms' => 'Gravity Forms',
	];

	public static function instance(): self {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	private function __construct() {
		add_action( 'rest_api_init', [ $this, 'register_routes' ] );
	}

	// ── REST routes ───────────────────────────────────────────────────────────

	public function register_routes(): void {
		register_rest_route( 'jalpi-page-builder/v1', '/forms/plugins', [
			'methods'             => WP_REST_Server::READABLE,
			'callback'            => [ $this, 'rest_get_plugins' ],
			'permission_callback' => fn() => current_user_can( 'edit_posts' ),
		] );

		register_rest_route( 'jalpi-page-builder/v1', '/forms', [
			'methods'             => WP_REST_Server::READABLE,
			'callback'            => [ $this, 'rest_get_forms' ],
			'permission_callback' => fn() => current_user_can( 'edit_posts' ),
			'args'                => [
				'plugin' => [
					'required'          => true,
					'type'              => 'string',
					'sanitize_callback' => 'sanitize_key',
				],
			],
		] );
	}

	/** Returns all currently active form plugins. */
	public function rest_get_plugins(): WP_REST_Response {
		$active = [];
		foreach ( self::PLUGINS as $slug => $_ ) {
			if ( $this->is_plugin_active( $slug ) ) {
				$active[] = [
					'value' => $slug,
					'label' => self::PLUGIN_LABELS[ $slug ] ?? $slug,
				];
			}
		}
		return rest_ensure_response( $active );
	}

	/** Returns all forms for the given plugin slug. */
	public function rest_get_forms( WP_REST_Request $request ): WP_REST_Response|WP_Error {
		$plugin = sanitize_key( $request->get_param( 'plugin' ) );

		if ( ! isset( self::PLUGINS[ $plugin ] ) ) {
			return new WP_Error( 'unknown_plugin', __( 'Unknown form plugin.', 'jalpi-page-builder' ), [ 'status' => 400 ] );
		}
		if ( ! $this->is_plugin_active( $plugin ) ) {
			return new WP_Error( 'plugin_inactive', __( 'Form plugin is not active.', 'jalpi-page-builder' ), [ 'status' => 400 ] );
		}

		$forms = match ( $plugin ) {
			'cf7'          => $this->get_cf7_forms(),
			'forminator'   => $this->get_forminator_forms(),
			'wpforms'      => $this->get_wpforms_forms(),
			'gravityforms' => $this->get_gravityforms_forms(),
			default        => [],
		};

		return rest_ensure_response( $forms );
	}

	// ── Plugin detection ──────────────────────────────────────────────────────

	public function is_plugin_active( string $slug ): bool {
		// is_plugin_active() lives in wp-admin/includes/plugin.php.
		// On frontend REST requests it may not be loaded yet.
		if ( ! function_exists( 'is_plugin_active' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}
		foreach ( (array) ( self::PLUGINS[ $slug ] ?? [] ) as $file ) {
			if ( is_plugin_active( $file ) ) {
				return true;
			}
		}
		return false;
	}

	// ── Form list helpers ─────────────────────────────────────────────────────

	private function get_cf7_forms(): array {
		if ( ! function_exists( 'wpcf7_contact_forms' ) ) {
			return [];
		}
		$forms = [];
		foreach ( wpcf7_contact_forms( [ 'posts_per_page' => -1, 'orderby' => 'title', 'order' => 'ASC' ] ) as $form ) {
			$forms[] = [ 'value' => (string) $form->id(), 'label' => $form->title() ];
		}
		return $forms;
	}

	private function get_forminator_forms(): array {
		if ( ! class_exists( 'Forminator_API' ) ) {
			return [];
		}
		$forms = [];
		$all   = Forminator_API::get_forms( null, 1, 200 );
		if ( ! is_array( $all ) ) {
			return [];
		}
		foreach ( $all as $form ) {
			$id    = $form->id;
			/* translators: %s: form ID number */
			$title = $form->settings['formName'] ?? ( $form->name ?? sprintf( __( 'Form #%s', 'jalpi-page-builder' ), $id ) );
			$forms[] = [ 'value' => (string) $id, 'label' => $title ];
		}
		return $forms;
	}

	private function get_wpforms_forms(): array {
		if ( ! function_exists( 'wpforms' ) ) {
			return [];
		}
		$all = wpforms()->form->get( '', [
			'posts_per_page' => -1,
			'orderby'        => 'post_title',
			'order'          => 'ASC',
		] );
		if ( ! $all || ! is_array( $all ) ) {
			return [];
		}
		$forms = [];
		foreach ( $all as $form ) {
			$forms[] = [
				'value' => (string) $form->ID,
				'label' => $form->post_title ?: "Form #{$form->ID}",
			];
		}
		return $forms;
	}

	private function get_gravityforms_forms(): array {
		if ( ! class_exists( 'GFAPI' ) ) {
			return [];
		}
		$all = GFAPI::get_forms( true ); // active forms only
		if ( ! is_array( $all ) ) {
			return [];
		}
		$forms = [];
		foreach ( $all as $form ) {
			$id      = $form['id'];
			/* translators: %s: form ID number */
			$forms[] = [ 'value' => (string) $id, 'label' => $form['title'] ?? sprintf( __( 'Form #%s', 'jalpi-page-builder' ), $id ) ];
		}
		return $forms;
	}

	// ── Rendering ─────────────────────────────────────────────────────────────

	/**
	 * Render a form using the plugin's native PHP API.
	 *
	 * Returns empty string when the plugin is inactive or the form ID is invalid.
	 */
	public static function render( string $plugin, int $form_id ): string {
		if ( $form_id < 1 ) {
			return '';
		}
		return match ( $plugin ) {
			'cf7'          => self::render_cf7( $form_id ),
			'forminator'   => self::render_forminator( $form_id ),
			'wpforms'      => self::render_wpforms( $form_id ),
			'gravityforms' => self::render_gravityforms( $form_id ),
			default        => '',
		};
	}

	private static function render_cf7( int $form_id ): string {
		if ( ! function_exists( 'wpcf7_contact_form' ) ) {
			return '';
		}
		$form = wpcf7_contact_form( $form_id );
		if ( ! ( $form instanceof WPCF7_ContactForm ) ) {
			return '';
		}
		// Ensure CF7's scripts + styles are enqueued (needed for AJAX submission).
		if ( function_exists( 'wpcf7_enqueue_scripts' ) ) {
			wpcf7_enqueue_scripts();
		}
		return $form->form_html();
	}

	private static function render_forminator( int $form_id ): string {
		// Try Forminator's front-end rendering class first.
		if ( class_exists( 'Forminator_CForm_Front' ) ) {
			$out = '';
			ob_start();
			try {
				Forminator_CForm_Front::get_instance()->handle_shortcode( [ 'id' => $form_id ], '' );
			} catch ( \Throwable $e ) {
				// swallow — fallback below
			} finally {
				$out = (string) ob_get_clean();
			}
			if ( '' !== trim( $out ) ) {
				return $out;
			}
		}
		// Fallback: invoke the registered shortcode handler directly.
		global $shortcode_tags;
		if ( isset( $shortcode_tags['forminator_form'] ) ) {
			$html = call_user_func( $shortcode_tags['forminator_form'], [ 'id' => $form_id ], '', 'forminator_form' );
			return is_string( $html ) ? $html : '';
		}
		return '';
	}

	private static function render_wpforms( int $form_id ): string {
		if ( ! function_exists( 'wpforms' ) ) {
			return '';
		}
		$wpf = wpforms();
		// WPForms Lite/Pro: use the frontend object's output() method.
		if ( isset( $wpf->frontend ) && method_exists( $wpf->frontend, 'output' ) ) {
			$out = '';
			ob_start();
			try {
				// output( id, display_title, display_desc )
				$wpf->frontend->output( $form_id, false, false );
			} finally {
				$out = (string) ob_get_clean();
			}
			if ( '' !== trim( $out ) ) {
				return $out;
			}
		}
		// Fallback: invoke the registered shortcode handler directly.
		global $shortcode_tags;
		if ( isset( $shortcode_tags['wpforms'] ) ) {
			$html = call_user_func( $shortcode_tags['wpforms'], [ 'id' => $form_id ], '', 'wpforms' );
			return is_string( $html ) ? $html : '';
		}
		return '';
	}

	private static function render_gravityforms( int $form_id ): string {
		if ( ! function_exists( 'gravity_form' ) ) {
			return '';
		}
		ob_start();
		try {
			// gravity_form( id, title, desc, tabindex, field_values, ajax, tabindex, echo )
			gravity_form( $form_id, false, false, false, null, true, 0, false );
		} catch ( \Throwable $e ) {
			ob_end_clean();
			return '';
		}
		return (string) ob_get_clean();
	}

	// ── Cache helpers ─────────────────────────────────────────────────────────

	/**
	 * Returns true if any node in the page tree is a form element.
	 * Used by the renderer to skip the transient cache — form plugins embed
	 * per-request nonces that become stale in cached HTML.
	 */
	public static function has_form_node( array $sections ): bool {
		foreach ( $sections as $section ) {
			if ( self::node_has_form( $section ) ) {
				return true;
			}
		}
		return false;
	}

	private static function node_has_form( array $node ): bool {
		if ( ( $node['type'] ?? '' ) === 'form' ) {
			return true;
		}
		foreach ( $node['children'] ?? [] as $child ) {
			if ( self::node_has_form( $child ) ) {
				return true;
			}
		}
		return false;
	}
}
