<?php

namespace GemBoards\API;

use GemBoards\Classes\Exception\GemboardsInvalidArgumentException;
use GemBoards\Collections\BoardCollection;
use GemBoards\Utils\Helper;
use WP_REST_Request;
use WP_REST_Server;

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


class Projects {

	/**
	 * 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 = 'projects';
		add_action( 'rest_api_init', [ $self, 'register_routes' ] );
	}

	public function register_routes() {
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>\d+)/stats',
			[
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_stats' ],
				'permission_callback' => function ( WP_REST_Request $request ) {
					return current_user_can( 'view_projects', [
						'project_id' => $request->get_param( 'id' ),
					] );
				},
				'args'                => [
					'id' => [
						'type'        => 'integer',
						'required'    => true,
						'description' => __( 'Project ID', 'gemboards' ),
					],
				],
			]
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>\d+)/recently-open',
			[
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_recently_open' ],
				'permission_callback' => function ( WP_REST_Request $request ) {
					return current_user_can( 'view_projects', [
						'project_id' => $request->get_param( 'id' ),
					] );
				},
				'args'                => [
					'id' => [
						'type'        => 'integer',
						'required'    => true,
						'description' => __( 'Project ID', 'gemboards' ),
					],
				],
			]
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>\d+)/bookmarks',
			[
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_bookmarks' ],
				'permission_callback' => function ( WP_REST_Request $request ) {
					return current_user_can( 'view_projects', [
						'project_id' => $request->get_param( 'id' ),
					] );
				},
				'args'                => [
					'id' => [
						'type'        => 'integer',
						'required'    => true,
						'description' => __( 'Project ID', 'gemboards' ),
					],
				],
			]
		);
	}

	public function get_stats( WP_REST_Request $request ) {
		$project_id = $request->get_param( 'id' );

		return rest_ensure_response( [
			'status_overview' => $this->get_status_overview( $project_id ),
		] );
	}

	private function get_status_overview( int $project_id ) {
		$cache_group  = 'gemboards_columns';
		$last_changed = wp_cache_get_last_changed( $cache_group );
		$cache_key    = "project_column_stats_$project_id:$last_changed";
		$has_cache    = wp_cache_get( $cache_key, $cache_group );
		if ( $has_cache ) {
			return $has_cache;
		}

		global $wpdb;
		$status_overview_data = $wpdb->get_results(
			$wpdb->prepare( "SELECT
					col.title,
					col.slug,
					COUNT(c.id) AS card_count
				FROM
					{$wpdb->prefix}gemboards_columns AS col
					LEFT JOIN {$wpdb->prefix}gemboards_cards AS c ON c.column_id = col.id
				WHERE
					col.board_id IN (
						SELECT
							board.id
						FROM
							{$wpdb->prefix}gemboards_boards AS board
						WHERE
							board.project_id = %d
					)
				GROUP BY
					col.title,
					col.slug", $project_id )
		);
		$data                 = [];
		foreach ( $status_overview_data as $status_overview_datum ) {
			$data[] = array_merge( (array) $status_overview_datum, [
				'card_count' => absint( $status_overview_datum->card_count ),
			] );
		}
		wp_cache_set( $cache_key, $data, $cache_group );

		return $data;
	}

	public function get_recently_open( WP_REST_Request $request ) {
		$user_id = get_current_user_id();

		try {
			$last_changed_board_visits = wp_cache_get_last_changed( 'gemboards_board_visits' );
			$last_changed_boards       = wp_cache_get_last_changed( 'gemboards_boards' );
			$last_changed_roles        = wp_cache_get_last_changed( 'gemboards_roles' );
			$cache_key                 = "recently_opened_boards_{$last_changed_boards}_{$last_changed_roles}_$last_changed_board_visits";
			$has_board_ids             = wp_cache_get( $cache_key, 'gemboards_board_visits' );

			if ( false === $has_board_ids ) {
				global $wpdb;
				// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				$board_ids = $wpdb->get_col( $wpdb->prepare( "
				SELECT
					board.id
				FROM
					{$wpdb->prefix}gemboards_board_visits board_visit
					INNER JOIN {$wpdb->prefix}gemboards_boards board ON board.id = board_visit.board_id
				WHERE board_visit.user_id = %d AND board.project_id = %d 
				ORDER BY board_visit.id DESC 
				LIMIT 5", get_current_user_id(), $request->get_param( 'id' )
				) );
				// phpcs:enable
				$board_ids = array_map( 'absint', $board_ids );
				wp_cache_set( $cache_key, $board_ids, 'gemboards_boards' );
			} else {
				$board_ids = $has_board_ids;
			}

			if ( empty( $board_ids ) ) {
				$boards = [];
			} else {
				$boards = new BoardCollection( [
					'per_page' => - 1,
					'where'    => [
						[
							'key'     => 'id',
							'value'   => $board_ids,
							'compare' => 'IN',
						],
					],
				] );
			}
		} catch ( GemboardsInvalidArgumentException $e ) {
			$boards = [];
			Helper::error_log( $e->getMessage() );
		}

		wp_send_json( array_map( fn( $board ) => array_merge($board->get_data(), [
			'id' => $board->get_id(),
		]), $boards->get_results() ) );
	}

	public function get_bookmarks( WP_REST_Request $request ) {
		$project_id   = $request->get_param( 'id' );
		$cache_group  = Helper::BOOKMARKS_TABLE_NAME;
		$last_changed = wp_cache_get_last_changed( $cache_group ) . ':' . wp_cache_get_last_changed( 'gemboards_projects' );
		$cache_key    = "bookmarks_for_project_$project_id:$last_changed";
		$has_cache    = wp_cache_get( $cache_key, $cache_group );
		if ( $has_cache ) {
			return rest_ensure_response( $has_cache );
		}

		global $wpdb;
		$table_name = $wpdb->prefix . Helper::BOOKMARKS_TABLE_NAME;
		// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name is hardcoded.
		$results = $wpdb->get_results(
			$wpdb->prepare( "SELECT
				bookmark.*,
				card.board_id AS card_board_id,
				COALESCE(board.title, task.post_title) AS title,
				task.ID AS task_id
			FROM
				$table_name bookmark
				INNER JOIN {$wpdb->prefix}gemboards_projects project ON project.id = bookmark.project_id
				LEFT JOIN {$wpdb->prefix}gemboards_boards board ON board.id = bookmark.target_object_id
				AND bookmark.target_object_type = 'board'
				LEFT JOIN {$wpdb->prefix}gemboards_cards card ON card.id = bookmark.target_object_id
				AND bookmark.target_object_type = 'card'
				LEFT JOIN $wpdb->posts task ON task.ID = card.task_id
				AND bookmark.target_object_type = 'card'
			WHERE
				bookmark.project_id = %d AND bookmark.user_id = %d
			ORDER BY
				bookmark.id DESC", $project_id, get_current_user_id() ) );
		// phpcs:enable
		$results = array_map( fn( $bookmark ) => array_merge( (array) $bookmark, [
			'id'               => absint( $bookmark->id ),
			'user_id'          => absint( $bookmark->user_id ),
			'project_id'       => absint( $bookmark->project_id ),
			'target_object_id' => absint( $bookmark->target_object_id ),
			'card_board_id'    => absint( $bookmark->card_board_id ),
			'task_id'          => absint( $bookmark->task_id ),
		]), $results);
		wp_cache_set( $cache_key, $results, $cache_group );

		return rest_ensure_response( $results );
	}

}
