<?php

namespace GemBoards\api;

use GemBoards\Utils\Helper;
use WP_Error;
use WP_REST_Request;
use WP_REST_Server;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
} // if accessed directly, exit


class Menu {
	/**
	 * The namespace of this controller's route.
	 *
	 * @var string
	 */
	protected string $namespace;

	/**
	 * The base of this controller's route.
	 *
	 * @var string
	 */
	protected string $rest_base;

	public static function init() {
		$self            = new self();
		$self->namespace = 'gemboards/v1';
		$self->rest_base = 'workspace-menu';
		add_action( 'rest_api_init', [ $self, 'register_routes' ] );
	}

	public function register_routes() {
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base,
			[
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_menu' ],
				'permission_callback' => function () {
					if ( ! current_user_can( 'read' ) ) {
						return new WP_Error(
							'rest_forbidden_context',
							esc_html__( 'Sorry, you are not allowed to access this API.', 'gemboards' ),
							array( 'status' => rest_authorization_required_code() )
						);
					}

					return true;
				},
				'args'                => [],
			],
		);
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/move-item',
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'move_menu_item' ],
				'permission_callback' => function ( WP_REST_Request $request ) {
					return current_user_can( 'update_projects', [
						'board_id' => $request->get_param( 'object_id' ),
					] );
				},
				'args'                => [
					'object_id'          => [
						'type'        => 'integer',
						'required'    => true,
						'description' => __( 'Object ID', 'gemboards' ),
					],
					'target_object_id'   => [
						'type'        => 'integer',
						'required'    => true,
						'description' => __( 'Target Object ID', 'gemboards' ),
					],
					'target_object_type' => [
						'type'        => 'string',
						'required'    => true,
						'enum'        => [ 'project', 'folder' ],
						'description' => __( 'Target Object Type', 'gemboards' ),
					],
				],
			],
		);
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/sort',
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [ $this, 'handle_project_sort' ],
				'permission_callback' => function () {
					if ( ! current_user_can( 'read' ) ) {
						return new WP_Error(
							'rest_forbidden_context',
							esc_html__( 'Sorry, you are not allowed to access this API.', 'gemboards' ),
							array( 'status' => rest_authorization_required_code() )
						);
					}
					return true;
				},
				'args'                => [
					'sort_order' => [
						'type'        => 'string',
						'required'    => false,
						'enum'        => [ 'ascending', 'descending', 'alphabetical' ],
						'description' => __( 'Project sort order', 'gemboards' ),
					],
				],
			],
		);
	}

	public function get_menu() {
		return rest_ensure_response( [
			'menu' => $this->get_menu_data(),
			'sort' => [
				'order' => $this->get_project_sort_order(),
			],
		] );
	}

	private function get_menu_data() {
		$user_id = get_current_user_id();

		$cache_key   = 'menu_for_user_' . $user_id;
		$cache_group = 'gemboards_workspace_user_menu';
		$has_cache   = wp_cache_get( $cache_key, $cache_group );
		if ( false !== $has_cache ) {
			return $has_cache;
		}

		global $wpdb;
		// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- There is no unfinished prepared here.
		if ( current_user_can( 'manage_gemboards' ) ) {
			$projects = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}gemboards_projects" );
			$folders  = $wpdb->get_results( "
				SELECT
					'folder' AS type,
					folder.id AS item_id,
					folder.project_id,
					folder.id AS folder_id,
					NULL AS board_type,
					folder.title AS item_title,
					NULL AS color,
					folder.created_at AS created_at
				FROM
					{$wpdb->prefix}gemboards_folders folder
			" );
			$boards   = $wpdb->get_results(
				"SELECT
					'board' AS type,
					b.id AS item_id,
					b.project_id,
					b.folder_id,
					b.board_type AS board_type,
					b.title AS item_title,
					b.icon_color AS color,
					b.created_at
				FROM
					{$wpdb->prefix}gemboards_boards b WHERE b.board_status = 'active'"
			);
		} else {
			$projects              = $wpdb->get_results(
				$wpdb->prepare( "
					SELECT project.* FROM {$wpdb->prefix}gemboards_projects project 
					    INNER JOIN {$wpdb->prefix}gemboards_roles role ON 
					        role.object_type = 'project' AND role.object_id = project.id AND role.user_id = %d
				", $user_id )
			);
			$project_ids           = array_map( fn( $project ) => $project->id, $projects );
			$project_ids_formatter = implode( ',', array_fill( 0, count( $project_ids ), '%d' ) );

			$folders = [];
			$boards  = [];

			if ( ! empty( $project_ids ) ) {
				$folders = $wpdb->get_results(
					$wpdb->prepare( "SELECT
					'folder' AS type,
					folder.id AS item_id,
					folder.project_id,
					folder.id AS folder_id,
					NULL AS board_type,
					folder.title AS item_title,
					NULL AS color,
					folder.created_at AS created_at
				FROM
					{$wpdb->prefix}gemboards_folders folder
					WHERE folder.project_id IN ($project_ids_formatter)", ...$project_ids )
				);
				$boards  = $wpdb->get_results(
					$wpdb->prepare( "SELECT
					'board' AS type,
					b.id AS item_id,
					b.project_id,
					b.folder_id,
					b.board_type AS board_type,
					b.title AS item_title,
					b.icon_color AS color,
					b.created_at
				FROM
					{$wpdb->prefix}gemboards_boards b
					WHERE b.board_status = 'active' AND b.project_id IN ($project_ids_formatter)", ...$project_ids )
				);
			}
		}
		// phpcs:enable

		$results = array_merge( $folders, $boards );
		usort( $results, fn( $a, $b ) => strcmp( $a->created_at, $b->created_at ) );

		$data = [];
		foreach ( $projects as $project ) {
			$settings                       = maybe_unserialize( $project->settings ) ?? [];
			$data[ absint( $project->id ) ] = [
				'id'              => absint( $project->id ),
				'name'            => $project->title,
				'slug'            => $project->slug,
				'description'     => $project->description,
				'icon'            => $project->icon,
				'color'           => $project->color,
				'background_type' => $settings['background_type'] ?? 'none',
				'background'      => $settings['background'] ?? null,
			];
		}

		foreach ( $results as $item ) {
			if ( 'folder' === $item->type ) {
				$dt = [
					'type'   => $item->type,
					'id'     => absint( $item->item_id ),
					'name'   => $item->item_title,
					'boards' => array_values(
						array_map(
							function ( $board_res ) {
								return [
									'id'    => absint( $board_res->item_id ),
									'name'  => $board_res->item_title,
									'type'  => $board_res->board_type,
									'color' => $board_res->color,
								];
							},
							array_filter( $results, fn( $res_item ) => 'board' === $res_item->type && $res_item->folder_id === $item->item_id )
						)
					),
				];
				$data[ absint( $item->project_id ) ]['folders'][] = $dt;
				continue;
			}

			if ( 'board' === $item->type && 0 === absint( $item->folder_id ) ) {
				$dt = [
					'id'    => absint( $item->item_id ),
					'name'  => $item->item_title,
					'type'  => $item->board_type,
					'color' => $item->color,
				];
				$data[ absint( $item->project_id ) ]['boards'][] = $dt;
			}
		}
		$data = array_values( $data );
		wp_cache_set( $cache_key, $data, $cache_group );

		return $data;
	}

	public function move_menu_item( WP_REST_Request $request ) {
		$object_id = $request->get_param( 'object_id' );
		$board     = Helper::get_board( $object_id );
		if ( ! $board ) {
			return new WP_Error( 'rest_invalid_param', __( 'Invalid object id.', 'gemboards' ), [
				'status' => 400,
				'params' => [ 'object_id' ],
			] );
		}

		$target_object_id   = $request->get_param( 'target_object_id' );
		$target_object_type = $request->get_param( 'target_object_type' );
		if ( 'folder' === $target_object_type ) {
			if ( ! current_user_can('update_projects', [
				'folder_id' => $target_object_id,
			]) ) {
				return new WP_Error( 'rest_forbidden_context', esc_html__( 'Sorry, you are not allowed to access this API.', 'gemboards' ), [ 'status' => rest_authorization_required_code() ]);
			}
			$folder = Helper::get_folder( $target_object_id );
			if ( ! $folder ) {
				return new WP_Error( 'rest_invalid_param', __( 'Invalid target object id.', 'gemboards' ), [
					'status' => 400,
					'params' => [ 'target_object_id' ],
				] );
			}
			$board->set_folder_id( $folder->get_id() );
		} else {
			if ( ! current_user_can('update_projects', [
				'project_id' => $target_object_id,
			]) ) {
				return new WP_Error( 'rest_forbidden_context', esc_html__( 'Sorry, you are not allowed to access this API.', 'gemboards' ), [ 'status' => rest_authorization_required_code() ]);
			}
			$project = Helper::get_project( $target_object_id );
			if ( ! $project ) {
				return new WP_Error( 'rest_invalid_param', __( 'Invalid target object id.', 'gemboards' ), [
					'status' => 400,
					'params' => [ 'target_object_id' ],
				] );
			}
			$board->set_folder_id( 0 );
			$board->set_project_id( $project->get_id() );
		}

		$board->save();

		return rest_ensure_response( [
			'success' => true,
			'menu'    => $this->get_menu_data(),
		] );
	}

	private function get_project_sort_order() {
		$user_id = get_current_user_id();
		$sort_order = get_user_meta( $user_id, 'gemboards_project_sort_order', true );
		if ( empty( $sort_order ) ) {
			$sort_order = 'ascending';
		}
		return $sort_order;
	}

	public function handle_project_sort( WP_REST_Request $request ) {
		$user_id = get_current_user_id();
		$sort_order = $request->get_param( 'sort_order' );

		if ( ! in_array( $sort_order, [ 'ascending', 'descending', 'alphabetical' ], true ) ) {
			return new WP_Error( 'rest_invalid_param', __( 'Invalid sort order.', 'gemboards' ), [
				'status' => 400,
				'params' => [ 'sort_order' ],
			] );
		}

		update_user_meta( $user_id, 'gemboards_project_sort_order', $sort_order );

		return rest_ensure_response( [
			'success' => true,
			'sort' => [
				'order' => $sort_order,
			],
		] );
	}
}
