<?php

/**
 * Class SiteOrigin_Panels_Admin
 *
 * Handles all the admin and database interactions.
 */
class SiteOrigin_Panels_Admin_Layouts {
	const LAYOUT_URL = 'https://layouts.siteorigin.com/';

	const VALID_MIME_TYPES = array(
		'application/json',
		'text/plain',
		'text/html',
	);

	public function __construct() {
		// Filter all the available external layout directories.
		add_filter( 'siteorigin_panels_external_layout_directories', array( $this, 'filter_directories' ), 8 );
		// Filter all the available local layout folders.
		add_filter( 'siteorigin_panels_prebuilt_layouts', array( $this, 'get_local_layouts' ), 8 );

		add_action( 'wp_ajax_so_panels_layouts_query', array( $this, 'action_get_prebuilt_layouts' ) );
		add_action( 'wp_ajax_so_panels_get_layout', array( $this, 'action_get_prebuilt_layout' ) );
		add_action( 'wp_ajax_so_panels_import_layout', array( $this, 'action_import_layout' ) );
		add_action( 'wp_ajax_so_panels_export_layout', array( $this, 'action_export_layout' ) );
		add_action( 'wp_ajax_so_panels_directory_enable', array( $this, 'action_directory_enable' ) );
	}

	/**
	 * @return SiteOrigin_Panels_Admin_Layouts
	 */
	public static function single() {
		static $single;

		return empty( $single ) ? $single = new self() : $single;
	}

	/**
	 * Add the main SiteOrigin layout directory
	 */
	public function filter_directories( $directories ) {
		if ( apply_filters( 'siteorigin_panels_layouts_directory_enabled', true ) ) {
			$directories['siteorigin'] = array(
				// The title of the layouts directory in the sidebar.
				'title' => __( 'Layouts Directory', 'siteorigin-panels' ),
				// The URL of the directory.
				'url'   => self::LAYOUT_URL,
				// Any additional arguments to pass to the layouts server
				'args'  => array(),
			);
		}

		return $directories;
	}

	/**
	 * Get all the layout directories.
	 *
	 * @return array
	 */
	public function get_directories() {
		$directories = apply_filters( 'siteorigin_panels_external_layout_directories', array() );

		if ( empty( $directories ) || ! is_array( $directories ) ) {
			$directories = array();
		}

		return $directories;
	}

	/**
	 * Check if the file has a valid MIME type.
	 *
	 * This method checks if the given file has a valid MIME type. It first verifies
	 * if the `mime_content_type` function exists. If it doesn't, it returns true
	 * as it can't check the MIME type due to server settings.
	 * If the function exists, it retrieves the MIME type of the file and checks
	 * if it is in the list of valid MIME types.
	 *
	 * @param string $file The file path to check.
	 *
	 * @return bool True if the file has a valid MIME type, false otherwise.
	 */
	private static function check_file_mime( $file ) {
		if ( ! function_exists( 'mime_content_type' ) ) {
			// Can't check mime type due to server settings.
			return true;
		}

		$mime_type = mime_content_type( $file );
		return in_array( $mime_type, self::VALID_MIME_TYPES );
	}

	/**
	 * Determines if file has a JSON extension.
	 *
	 * @param string $file File path.
	 * @return bool True if JSON, false otherwise.
	 */
	private static function check_file_ext( $file ) {
		$ext = pathinfo( $file, PATHINFO_EXTENSION );
		return ! empty( $ext ) && $ext === 'json';
	}

	/**
	 * Looks through local folders in the active theme and any others filtered in by theme and plugins, to find JSON
	 * prebuilt layouts.
	 */
	public function get_local_layouts() {
		// By default we'll look for layouts in a directory in the active theme
		$layout_folders = array( get_template_directory() . '/siteorigin-page-builder-layouts' );

		// And the child theme if there is one.
		if ( is_child_theme() ) {
			$layout_folders[] = get_stylesheet_directory() . '/siteorigin-page-builder-layouts';
		}

		// This allows themes and plugins to customize where we look for layouts.
		$layout_folders = apply_filters( 'siteorigin_panels_local_layouts_directories', $layout_folders );

		$layouts = array();

		foreach ( $layout_folders as $folder ) {
			$folder = realpath( $folder );

			if ( file_exists( $folder ) && is_dir( $folder ) ) {
				$files = list_files( $folder, 1 );

				if ( empty( $files ) ) {
					continue;
				}

				foreach ( $files as $file ) {
					// Check the file.
					if (
						! self::check_file_mime( $file ) ||
						! self::check_file_ext( $file )
					) {
						continue;
					}

					// get file contents
					$file_contents = file_get_contents( $file );

					// skip if file_get_contents fails
					if ( $file_contents === false ) {
						continue;
					}

					$panels_data = $this->decode_panels_data( $file_contents );

					if ( empty( $panels_data ) ) {
						continue;
					}

					// get file name by stripping out folder path and .json extension
					$file_name = str_replace( array( $folder . '/', '.json' ), '', $file );

					// get name: check for id or name else use filename
					$panels_data['id'] = sanitize_title_with_dashes( $this->get_layout_id( $panels_data, $file_name ) );

					if ( empty( $panels_data['name'] ) ) {
						$panels_data['name'] = $file_name;
					}

					$panels_data['name'] = sanitize_text_field( $panels_data['name'] );

					// get screenshot: check for screenshot prop else try use image file with same filename.
					$panels_data['screenshot'] = $this->get_layout_file_screenshot( $panels_data, $folder, $file_name );

					// set item on layouts array
					$layouts[ $panels_data['id'] ] = $panels_data;
				}
			}
		}

		return $layouts;
	}

	private function get_layout_id( $layout_data, $fallback ) {
		if ( ! empty( $layout_data['id'] ) ) {
			return $layout_data['id'];
		} elseif ( ! empty( $layout_data['name'] ) ) {
			return $layout_data['name'];
		} else {
			return $fallback;
		}
	}

	private static function get_files( $folder_path, $file_name ) {
		$paths = array();
		$types = array(
			'jpg',
			'jpeg',
			'gif',
			'png',
		);

		foreach ( $types as $ext ) {
			$paths = array_merge( glob( $folder_path . "/$file_name.$ext" ), $paths );
		}

		return $paths;
	}

	private function get_layout_file_screenshot( $panels_data, $folder_path, $file_name ) {
		if ( ! empty( $panels_data['screenshot'] ) ) {
			return $panels_data['screenshot'];
		} else {
			$paths = self::get_files( $folder_path, $file_name );
			// Highlander Condition. There can be only one.
			$screenshot_path = empty( $paths ) ? '' : wp_normalize_path( $paths[0] );
			$wp_content_dir = wp_normalize_path( WP_CONTENT_DIR );
			$screenshot_url = '';

			if ( file_exists( $screenshot_path ) &&
				 strrpos( $screenshot_path, $wp_content_dir ) === 0 ) {
				$screenshot_url = str_replace( $wp_content_dir, content_url(), $screenshot_path );
			}

			return $screenshot_url;
		}
	}

	/**
	 * Gets all the prebuilt layouts.
	 */
	public function action_get_prebuilt_layouts() {
		if ( empty( $_REQUEST['_panelsnonce'] ) || ! wp_verify_nonce( $_REQUEST['_panelsnonce'], 'panels_action' ) ) {
			wp_die( __( 'Invalid request.', 'siteorigin-panels' ), 403 );
		}

		// Get any layouts that the current user could edit.
		header( 'content-type: application/json' );

		$type = ! empty( $_REQUEST['type'] ) ? $_REQUEST['type'] : 'directory-siteorigin';
		$search = ! empty( $_REQUEST['search'] ) ? trim( strtolower( $_REQUEST['search'] ) ) : '';
		$page_num = ! empty( $_REQUEST['page'] ) ? (int) $_REQUEST['page'] : 1;

		$return = array(
			'title' => '',
			'items' => array(),
		);

		if ( $type == 'prebuilt' ) {
			$return['title'] = __( 'Theme Defined Layouts', 'siteorigin-panels' );

			// This is for theme bundled prebuilt directories
			$layouts = apply_filters( 'siteorigin_panels_prebuilt_layouts', array() );

			foreach ( $layouts as $id => $vals ) {
				if ( ! empty( $search ) && strpos( strtolower( $vals['name'] ), $search ) === false ) {
					continue;
				}

				$return['items'][] = array(
					'title'       => esc_html( $vals['name'] ),
					'id'          => esc_html( $id ),
					'type'        => 'prebuilt',
					'description' => isset( $vals['description'] ) ? esc_html( $vals['description'] ) : '',
					'screenshot'  => ! empty( $vals['screenshot'] ) ? esc_url( $vals['screenshot'] ) : '',
				);
			}

			$return['max_num_pages'] = 1;
		} elseif ( substr( $type, 0, 10 ) == 'directory-' ) {
			$return['title'] = __( 'Layouts Directory', 'siteorigin-panels' );

			// This is a query of the prebuilt layout directory
			$query = array();

			if ( ! empty( $search ) ) {
				$query['search'] = $search;
			}
			$query['page'] = $page_num;

			$directory_id = str_replace( 'directory-', '', $type );
			$directories = $this->get_directories();
			$directory = ! empty( $directories[ $directory_id ] ) ? $directories[ $directory_id ] : false;

			if ( empty( $directory ) ) {
				return false;
			}

			$cache = get_transient( 'siteorigin_panels_layouts_directory_' . $directory_id .'_page_' . $page_num );

			if ( empty( $search ) && ! empty( $cache ) ) {
				$return = $cache;
			} else {
				$url = add_query_arg( $query, $directory[ 'url' ] . 'wp-admin/admin-ajax.php?action=query_layouts' );

				if ( ! empty( $directory['args'] ) && is_array( $directory['args'] ) ) {
					$url = add_query_arg( $directory['args'], $url );
				}

				$url = apply_filters( 'siteorigin_panels_layouts_directory_url', $url );
				$response = wp_remote_get( esc_url_raw( $url ) );

				if (
					! is_wp_error( $response ) &&
					is_array( $response ) &&
					$response['response']['code'] == 200
				) {
					$results = json_decode( $response['body'], true );

					if ( ! empty( $results ) && ! empty( $results['items'] ) ) {
						foreach ( $results['items'] as $item ) {
							$item['id'] = esc_html( $item['slug'] );
							$item['type'] = esc_html( $type );

							if ( empty( $item['screenshot'] ) && ! empty( $item['preview'] ) ) {
								$preview_url = add_query_arg( 'screenshot', 'true', $item[ 'preview' ] );
								$item['screenshot'] = esc_url( 'https://s.wordpress.com/mshots/v1/' . urlencode( $preview_url ) . '?w=700' );
							}

							$return['items'][] = $item;
						}
					}

					$return['max_num_pages'] = $results['max_num_pages'];
					set_transient( 'siteorigin_panels_layouts_directory_' . $directory_id .'_page_' . $page_num, $return, 86400 );
				}
			}
		} elseif ( strpos( $type, 'clone_' ) !== false ) {
			$post_type = str_replace( 'clone_', '', $type );
			$post_types_editable_by_user = SiteOrigin_Panels_Admin_Layouts::single()->post_types();

			// Can the user edit posts from this post type?
			if (
				empty( $post_type ) ||
				empty( $post_types_editable_by_user ) ||
				! in_array(
					$post_type,
					$post_types_editable_by_user
				)
			) {
				return;
			}

			$post_type = get_post_type_object( $post_type );
			if ( empty( $post_type ) ) {
				return;
			}

			$return['title'] = sprintf( __( 'Clone %s', 'siteorigin-panels' ), esc_html( $post_type->labels->singular_name ) );

			global $wpdb;
			$user_can_read_private = ( $post_type->name == 'post' && current_user_can( 'read_private_posts' ) || ( $post_type->name == 'page' && current_user_can( 'read_private_pages' ) ) );
			$include_private = $user_can_read_private ? "OR posts.post_status = 'private' " : '';

			// Select only the posts with the given post type that also have panels_data
			$results = $wpdb->get_results( "
				SELECT SQL_CALC_FOUND_ROWS DISTINCT ID, post_title, meta.meta_value
				FROM {$wpdb->posts} AS posts
				JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
				WHERE
					posts.post_type = '" . esc_sql( $post_type->name ) . "'
					AND meta.meta_key = 'panels_data'
					" . ( ! empty( $search ) ? 'AND posts.post_title LIKE "%' . esc_sql( $search ) . '%"' : '' ) . "
					AND ( posts.post_status = 'publish' OR posts.post_status = 'draft' " . $include_private . ')
				ORDER BY post_date DESC
				LIMIT 16 OFFSET ' . (int) ( $page_num - 1 ) * 16 );
			$total_posts = $wpdb->get_var( 'SELECT FOUND_ROWS();' );

			foreach ( $results as $result ) {
				$thumbnail = get_the_post_thumbnail_url( (int) $result->ID, array( 400, 300 ) );
				$return['items'][] = array(
					'id'         => (int) $result->ID,
					'title'      => esc_html( $result->post_title ),
					'type'       => esc_html( $type ),
					'screenshot' => ! empty( $thumbnail ) ? esc_url( $thumbnail ) : '',
				);
			}

			$return['max_num_pages'] = ceil( $total_posts / 16 );
		} else {
			// An invalid type. Display an error message.
		}

		// Add the search part to the title
		if ( ! empty( $search ) ) {
			$return['title'] .= __( ' - Results For:', 'siteorigin-panels' ) . ' <em>' . esc_html( $search ) . '</em>';
		}

		echo wp_json_encode( apply_filters( 'siteorigin_panels_layouts_result', $return, $type ) );

		wp_die();
	}

	private function delete_file( $file ) {
		if ( ! empty( $file ) && file_exists( $file ) ) {
			@unlink( $file );
		}
	}

	public function decode_panels_data( $data, $file = null ) {
		$panels_data = json_decode( $data, true );

		if ( json_last_error() !== JSON_ERROR_NONE ) {
			$this->delete_file( $file );
			wp_die();
		}

		// Newer exports may require further decoding.
		if ( ! is_array( $panels_data ) ) {
			$panels_data = $this->decode_panels_data( $panels_data );
		}

		$panels_data = wp_unslash( $panels_data );
		$this->delete_file( $file );
		return $panels_data;
	}

	/**
	 * Ajax handler to get an individual prebuilt layout
	 */
	public function action_get_prebuilt_layout() {
		if ( empty( $_REQUEST['type'] ) ) {
			wp_die();
		}

		if ( ! isset( $_REQUEST['lid'] ) ) {
			wp_die();
		}

		if ( empty( $_REQUEST['_panelsnonce'] ) || ! wp_verify_nonce( $_REQUEST['_panelsnonce'], 'panels_action' ) ) {
			wp_die();
		}

		header( 'content-type: application/json' );
		$panels_data = array();
		$raw_panels_data = false;

		if ( $_REQUEST['type'] == 'prebuilt' ) {
			$layouts = apply_filters( 'siteorigin_panels_prebuilt_layouts', array() );

			if (
				! is_numeric( $_REQUEST['lid'] ) &&
				empty( $layouts[ $_REQUEST['lid'] ] )
			) {
				wp_send_json_error( array(
					'error'   => true,
					'message' => __( 'Missing layout ID or no such layout exists', 'siteorigin-panels' ),
				) );
			}

			$layout = $layouts[ $_REQUEST['lid'] ];

			// Fix the format of this layout
			if ( ! empty( $layout[ 'filename' ] ) ) {
				$filename = $layout[ 'filename' ];
				// Only accept filenames that end with .json
				if ( substr( $filename, -5, 5 ) === '.json' && file_exists( $filename ) ) {
					$panels_data = json_decode( file_get_contents( $filename ), true );
					$layout[ 'widgets' ] = ! empty( $panels_data[ 'widgets' ] ) ? $panels_data[ 'widgets' ] : array();
					$layout[ 'grids' ] = ! empty( $panels_data[ 'grids' ] ) ? $panels_data[ 'grids' ] : array();
					$layout[ 'grid_cells' ] = ! empty( $panels_data[ 'grid_cells' ] ) ? $panels_data[ 'grid_cells' ] : array();
				}
			}

			// A theme or plugin could use this to change the data in the layout
			$panels_data = apply_filters( 'siteorigin_panels_prebuilt_layout', $layout, $_REQUEST['lid'] );

			// Remove all the layout specific attributes
			if ( isset( $panels_data['name'] ) ) {
				unset( $panels_data['name'] );
			}

			if ( isset( $panels_data['screenshot'] ) ) {
				unset( $panels_data['screenshot'] );
			}

			if ( isset( $panels_data['filename'] ) ) {
				unset( $panels_data['filename'] );
			}

			$raw_panels_data = true;
		} elseif ( substr( $_REQUEST['type'], 0, 10 ) == 'directory-' ) {
			$directory_id = str_replace( 'directory-', '', $_REQUEST['type'] );

			$directories = $this->get_directories();
			$directory = ! empty( $directories[ $directory_id ] ) ? $directories[ $directory_id ] : false;

			if ( ! empty( $directory ) ) {
				$url = $directory[ 'url' ] . 'layout/' . urlencode( $_REQUEST[ 'lid' ] ) . '/?action=download';

				if ( ! empty( $directory[ 'args' ] ) && is_array( $directory[ 'args' ] ) ) {
					$url = add_query_arg( $directory[ 'args' ], $url );
				}

				$response = wp_remote_get( $url );

				if ( $response['response']['code'] == 200 ) {
					// For now, we'll just pretend to load this
					$panels_data = json_decode( $response['body'], true );
				} else {
					wp_send_json_error( array(
						'error'   => true,
						'message' => __( 'There was a problem fetching the layout. Please try again later.', 'siteorigin-panels' ),
					) );
				}
			}
			$raw_panels_data = true;
		} elseif ( current_user_can( 'edit_post', $_REQUEST['lid'] ) ) {
			$panels_data = get_post_meta( $_REQUEST['lid'], 'panels_data', true );

			// Clear id and timestamp for SO widgets to prevent 'newer content version' notification in widget forms.
			foreach ( $panels_data['widgets'] as &$widget ) {
				unset( $widget['_sow_form_id'] );
				unset( $widget['_sow_form_timestamp'] );
			}
		}

		if ( $raw_panels_data ) {
			// This panels_data is flagged as raw, so it needs to be processed.
			$panels_data = apply_filters( 'siteorigin_panels_data', $panels_data, false );
			$panels_data['widgets'] = SiteOrigin_Panels_Admin::single()->process_raw_widgets( $panels_data['widgets'], array(), true, true );
		}

		wp_send_json_success( $panels_data );
	}

	/**
	 * Ajax handler to import a layout
	 */
	public function action_import_layout() {
		if (
			empty( $_REQUEST['_panelsnonce'] ) ||
			! wp_verify_nonce( $_REQUEST['_panelsnonce'], 'panels_action' )
		) {
			wp_die();
		}

		// Ensure there wasn't an error during upload.
		if (
			empty( $_FILES['panels_import_data']['tmp_name'] ) ||
			! file_exists( $_FILES['panels_import_data']['tmp_name'] )
		) {
			wp_die();
		}

		$json = file_get_contents( $_FILES['panels_import_data']['tmp_name'] );
		$panels_data = $this->decode_panels_data( $json, $_FILES['panels_import_data']['tmp_name'] );

		header( 'content-type:application/json' );
		$panels_data = apply_filters( 'siteorigin_panels_data', $panels_data, false );
		$panels_data['widgets'] = SiteOrigin_Panels_Admin::single()->process_raw_widgets( $panels_data['widgets'], array(), true, true );
		echo wp_json_encode( $panels_data );
		wp_die();
	}

	/**
	 * Export a given layout as a JSON file.
	 */
	public function action_export_layout() {
		if ( empty( $_REQUEST['_panelsnonce'] ) || ! wp_verify_nonce( $_REQUEST['_panelsnonce'], 'panels_action' ) ) {
			wp_die();
		}

		$export_data = wp_unslash( $_POST['panels_export_data'] );
		$decoded_export_data = $this->decode_panels_data( $export_data );

		if ( ! empty( $decoded_export_data['name'] ) ) {
			$decoded_export_data['id'] = sanitize_title_with_dashes( $decoded_export_data['name'] );
			$filename = sanitize_file_name( $decoded_export_data['id'] );
		} else {
			$filename = 'layout-' . date( 'dmY' );
		}

		header( 'content-type: application/json' );
		header( "Content-Disposition: attachment; filename=$filename.json" );

		echo wp_json_encode( $export_data );

		wp_die();
	}

	/**
	 * Enable the directory.
	 */
	public function action_directory_enable() {
		if ( empty( $_REQUEST['_panelsnonce'] ) || ! wp_verify_nonce( $_REQUEST['_panelsnonce'], 'panels_action' ) ) {
			wp_die();
		}
		$user = get_current_user_id();
		update_user_meta( $user, 'so_panels_directory_enabled', true );
		wp_die();
	}

	/**
	 * Load a layout from a json file
	 *
	 * @param bool $screenshot
	 *
	 * @return array The data for the layout
	 */
	public function load_layout( $id, $name, $json_file, $screenshot = false ) {
		$json = file_get_contents( $json_file );
		$layout_data = $this->decode_panels_data( $json );

		$layout_data = apply_filters( 'siteorigin_panels_load_layout_' . $id, $layout_data );

		$layout_data = array_merge( array(
			'name' => sanitize_text_field( $name ),
			'screenshot' => esc_url( $screenshot ),
		), $layout_data );

		return $layout_data;
	}

	/**
	 * Get the post types that the current user can edit.
	 *
	 * This function retrieves the post types specified in the
	 * SiteOrigin Panels settings. It then filters out post types that the
	 * current user does not have permission to edit.
	 *
	 * @return array The post types that the current user can edit.
	 */
	public function post_types() {
		$post_types = siteorigin_panels_setting( 'post-types' );
		if ( empty( $post_types ) ) {
			return array();
		}

		foreach ( $post_types as $id => $post_type ) {
			$post_type_object = get_post_type_object( $post_type );

			if (
				empty( $post_type_object ) ||
				! current_user_can( $post_type_object->cap->edit_posts )
			) {
				unset( $post_types[ $id ] );
			}
		}

		return $post_types;
	}
}
