<?php

namespace GemBoards\Ajax;

use GemBoards\Classes\AbstractAjaxHandler;
use GemBoards\Classes\Exception\GemboardsException;
use GemBoards\Classes\Exception\GemboardsInvalidArgumentException;
use GemBoards\Classes\PermissionResolver;
use GemBoards\Collections\BoardCollection;
use GemBoards\Collections\CardCollection;
use GemBoards\Collections\ProjectCollection;
use GemBoards\Collections\RoleCollection;
use GemBoards\Collections\UserInvitationCollection;
use GemBoards\Entities\CardAssignee;
use GemBoards\Entities\Column;
use GemBoards\Entities\Notification;
use GemBoards\Entities\Role;
use GemBoards\Entities\TaskAttachment;
use GemBoards\Entities\UserInvitation;
use GemBoards\Utils\Helper;
use GemBoardsPro\Entities\TaskChecklist;
use GemBoardsPro\Entities\TaskChecklistGroup;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Project extends AbstractAjaxHandler {

	public function __construct() {
		$this->actions = [
			'create_project'             => [
				'callback' => [ $this, 'create_project' ],
				'fields'   => [
					'id'              => 'integer',
					'title'           => 'string',
					'slug'            => 'string',
					'description'     => 'string',
					'icon'            => 'string',
					'color'           => 'string',
					'owner_id'        => 'integer',
					'background_type' => 'string',
					'background'      => 'string',
				],
			],
			'get_project'                => [
				'callback' => [ $this, 'get_project' ],
				'fields'   => [ 'id' => 'integer' ],
			],
			'get_projects'               => [
				'callback' => [ $this, 'get_projects' ],
			],
			'get_project_members'        => [
				'callback'   => [ $this, 'get_project_members' ],
				'permission' => 'view_projects',
				'fields'     => [ 'project_id' => 'integer' ],
			],
			'add_project_member'         => [
				'callback' => [ $this, 'add_member' ],
				'fields'   => [
					'id'      => 'integer',
					'user_id' => 'integer',
					'role'    => 'string',
				],
			],
			'update_project_member_role' => [
				'callback'   => [ $this, 'update_project_member_role' ],
				'permission' => 'update_projects',
				'fields'     => [
					'project_id' => 'integer',
					'role_id'    => 'integer',
					'role'       => 'string',
				],
			],
			'remove_project_member'      => [
				'callback' => [ $this, 'remove_member' ],
				'fields'   => [
					'id' => 'integer',
				],
			],
			'send_new_user_invitation'   => [
				'callback'   => [ $this, 'send_new_user_invitation' ],
				'permission' => 'update_projects',
				'fields'     => [
					'project_id' => 'integer',
					'email'      => 'email',
				],
			],
			'remove_new_user_invitation' => [
				'callback' => [ $this, 'remove_new_user_invitation' ],
				'fields'   => [
					'id' => 'integer',
				],
			],
			'delete_project'             => [
				'callback'   => [ $this, 'delete_project' ],
				'capability' => 'manage_gemboards',
				'fields'     => [ 'id' => 'integer' ],
			],
		];
	}

	public function create_project( $request ) {
		if ( empty( $request['title'] ) ) {
			wp_send_json_error( [ 'message' => __( 'Project title is required.', 'gemboards' ) ] );
		}

		$id              = $request['id'] ?? 0;
		$title           = $request['title'];
		$slug            = $request['slug'] ?? sanitize_title( $title );
		$desc            = $request['description'] ?? '';
		$icon            = $request['icon'] ?? mb_substr( trim( $title ), 0, 1, 'UTF-8' );
		$color           = $request['color'] ?? null;
		$owner           = $request['owner_id'] ?? get_current_user_id();
		$background_type = $request['background_type'] ?? 'none';
		$background      = $request['background'] ?? null;

		if ( $id ) {
			if ( ! PermissionResolver::can( 'update_projects', [
				'project_id' => $id,
			] ) ) {
				wp_send_json_error( [
					'message' => __( 'You do not have permission to update projects.', 'gemboards' ),
				] );
			}
		} else {
			if ( ! current_user_can( 'manage_gemboards' ) ) {
				wp_send_json_error( [
					'message' => __( 'You do not have permission to create projects.', 'gemboards' ),
				] );
			}
		}

		if ( $id ) {
			$project = Helper::get_project( $id );
			if ( ! $project ) {
				wp_send_json_error( [
					'message' => __( 'Invalid project id.', 'gemboards' ),
				] );
			}
		} else {
			$project = new \GemBoards\Entities\Project();
		}
		$project->set_title( $title );
		$project->set_slug( $slug );
		$project->set_description( $desc );
		$project->set_icon( $icon );
		$project->set_color( $color );
		$project->set_owner_id( $owner );
		$project->set_background_type( $background_type );
		$project->set_background( $background );
		$result = $project->save();
		if ( is_wp_error( $result ) ) {
			wp_send_json_error( [
				'message' => $result->get_error_message(),
			] );
		}

		if ( ! $id ) {
			$role = new Role();
			$role->set_object_id( $project->get_id() );
			$role->set_user_id( $owner );
			$role->set_role( 'manager' );
			$role->save();
		}

		wp_send_json_success( $this->get_project_data( $project ) );
	}

	public function get_project( $request ) {
		$id = $request['id'] ?? 0;
		if ( empty( $id ) ) {
			wp_send_json_error( [ 'message' => __( 'Project ID is required.', 'gemboards' ) ] );
		}

		if ( ! current_user_can( 'view_projects', [
			'project_id' => $id,
		] ) ) {
			wp_send_json_error( [
				'message' => __( 'You do not have permission to view projects.', 'gemboards' ),
			] );
		}

		$project = Helper::get_project( $id );
		if ( ! $project ) {
			wp_send_json_error( [
				'message' => __( 'Invalid project id.', 'gemboards' ),
			] );
		}

		wp_send_json_success( $this->get_project_data( $project ) );
	}

	public function get_projects() {
		try {
			$user_id = get_current_user_id();

			$args = [
				'per_page' => - 1,
			];
			if ( ! current_user_can( 'manage_gemboards' ) ) {
				$roles = Helper::get_user_roles( $user_id );
				if ( empty( $roles ) ) {
					wp_send_json_success( [] );
				}
				$project_ids = array_map( fn( $role ) => $role->get_object_id(), $roles );

				$args['where'] = [
					[
						'key'     => 'id',
						'value'   => $project_ids,
						'compare' => 'IN',
					],
				];
			}

			$projects = new ProjectCollection( $args );
			wp_send_json_success(
				array_map(
					fn( $project ) => $this->get_project_data( $project ),
					$projects->get_results()
				)
			);
		} catch ( GemboardsInvalidArgumentException $e ) {
			wp_send_json_error( [
				'message' => $e->getMessage(),
				'status'  => 500,
			] );
		}
	}

	public function get_project_members( array $payload ) {
		$project_id = $payload['project_id'] ?? 0;
		if ( empty( $project_id ) ) {
			wp_send_json_error( [ 'message' => __( 'Project ID is required.', 'gemboards' ) ] );
		}

		try {
			$roles = ( new RoleCollection( [
				'per_page' => - 1,
				'where'    => [
					[
						'key'   => 'object_type',
						'value' => 'project',
					],
					[
						'key'   => 'object_id',
						'value' => $project_id,
					],
				],
			] ) )->get_results();

			/** @var Role[] $roles */
			$roles = array_map( function ( $role ) {
				$user = get_user( $role->get_user_id() );

				if ( ! $user ) {
					return array_merge( $role->get_data(), [
						'user' => null,
					] );
				}

				return array_merge( $role->get_data(), [
					'id'   => $role->get_id(),
					'user' => [
						'first_name'   => $user->first_name,
						'last_name'    => $user->last_name,
						'email'        => $user->user_email,
						'display_name' => $user->display_name,
						'username'     => $user->user_login,
						'avatar'       => get_avatar_url( $user->ID ),
					],
				] );
			}, $roles );

			/** @var UserInvitation[] $invitations */
			$invitations = ( new UserInvitationCollection( [
				'per_page' => - 1,
				'where'    => [
					[
						'key'   => 'object_id',
						'value' => $project_id,
					],
					[
						'key'   => 'object_type',
						'value' => 'project',
					],
				],
			] ) )->get_results();
			$invitations = array_map( fn( $invitation ) => [
				'id'               => $invitation->get_id(),
				'email'            => $invitation->get_email(),
				'email_sent_count' => $invitation->get_email_sent_count(),
			], $invitations );

			wp_send_json_success( [
				'roles'       => $roles,
				'invitations' => $invitations,
			] );
		} catch ( GemboardsInvalidArgumentException $e ) {
			Helper::error_log( $e->getMessage() );
			wp_send_json_error( [
				'message' => $e->getMessage(),
			] );
		}
	}

	public function add_member( array $payload ) {
		if ( empty( $payload['id'] ) ) {
			wp_send_json_error( [ 'message' => __( 'Project ID is required.', 'gemboards' ) ] );
		}

		if ( empty( $payload['user_id'] ) ) {
			wp_send_json_error( [ 'message' => __( 'User ID is required.', 'gemboards' ) ] );
		}

		$project_id = absint( $payload['id'] );
		if ( ! current_user_can( 'update_projects', [
			'project_id' => $project_id,
		] ) ) {
			wp_send_json_error( [ 'message' => __( 'You do not have permission to add member in this project.', 'gemboards' ) ] );
		}

		$user = get_user( $payload['user_id'] );
		if ( ! $user ) {
			wp_send_json_error( [ 'message' => __( 'User not found.', 'gemboards' ) ] );
		}

		$has_permission = Helper::get_user_project_role( $project_id, $user->ID );
		if ( $has_permission ) {
			wp_send_json_error( [ 'message' => __( 'Permission already exists.', 'gemboards' ) ] );
		}

		$allowed_roles = [ 'admin', 'member', 'manager' ];
		if ( ! in_array( $payload['role'] ?? 'member', $allowed_roles, true ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid role.', 'gemboards' ) ] );
		}

		$role = new Role();
		$role->set_object_id( $project_id );
		$role->set_user_id( $user->ID );
		$role->set_role( $payload['role'] ?? 'member' );
		$result = $role->save();
		if ( is_wp_error( $result ) ) {
			wp_send_json_error( [
				'message' => $result->get_error_message(),
			] );
		}

		( new \GemBoards\Entities\Board() )->clear_cache();
		wp_send_json_success( array_merge( $role->get_data(), [
			'id' => $role->get_id(),
		]) );
	}

	public function update_project_member_role( array $payload ) {
		if ( empty( $payload['project_id'] ) ) {
			wp_send_json_error( [ 'message' => __( 'Project ID is required.', 'gemboards' ) ] );
		}

		if ( empty( $payload['role_id'] ) ) {
			wp_send_json_error( [ 'message' => __( 'Role ID is required.', 'gemboards' ) ] );
		}

		$role = Helper::get_role( $payload['role_id'] );
		if ( ! $role || $role->get_object_id() !== $payload['project_id'] ) {
			wp_send_json_error( [
				'message' => __( 'Invalid role id.', 'gemboards' ),
			] );
		}
		$allowed_roles  = [ 'admin', 'member', 'manager' ];
		$requested_role = empty( $payload['role'] ) ? 'member' : $payload['role'];
		if ( ! in_array( $requested_role, $allowed_roles, true ) ) {
			wp_send_json_error( [ 'message' => __( 'Invalid role.', 'gemboards' ) ] );
		}

		$role->set_role( $requested_role );
		$role->save();
		( new \GemBoards\Entities\Board() )->clear_cache();

		wp_send_json_success( array_merge( $role->get_data(), [
			'id' => $role->get_id(),
		]) );
	}

	public function remove_member( array $payload ) {
		if ( empty( $payload['id'] ) ) {
			wp_send_json_error( [ 'message' => __( 'Role ID is required.', 'gemboards' ) ] );
		}

		if ( ! current_user_can( 'update_projects', [
			'role_id' => $payload['id'],
		] ) ) {
			wp_send_json_error( [ 'message' => __( 'You do not have permission to remove member from this project.', 'gemboards' ) ] );
		}

		$role = Helper::get_role( $payload['id'] );
		if ( ! $role ) {
			wp_send_json_error( [
				'message' => __( 'Invalid role id.', 'gemboards' ),
			] );
		}

		try {
			$role->delete();
			( new \GemBoards\Entities\Board() )->clear_cache();
			wp_send_json_success();
		} catch ( GemboardsException $e ) {
			wp_send_json_error( [
				'message' => $e->get_wp_error()->get_error_message(),
			] );
		}
	}

	public function send_new_user_invitation( array $payload ) {
		if ( empty( $payload['project_id'] ) ) {
			wp_send_json_error( [ 'message' => __( 'Project ID is required.', 'gemboards' ) ] );
		}
		if ( empty( $payload['email'] ) ) {
			wp_send_json_error( [ 'message' => __( 'Email is required.', 'gemboards' ) ] );
		}

		$project_id = absint( $payload['project_id'] );
		$project    = Helper::get_project( $project_id );
		if ( ! $project ) {
			wp_send_json_error( [ 'message' => __( 'Invalid Project ID.', 'gemboards' ) ] );
		}

		$email = $payload['email'];
		$user  = get_user_by( 'email', $email );
		if ( $user ) {
			wp_send_json_error( [ 'message' => __( 'User already found.', 'gemboards' ) ] );
		}

		$cache_group   = 'gemboards_user_invitations';
		$last_changed  = wp_cache_get_last_changed( $cache_group );
		$cache_key     = "user_invitation_id_{$project_id}_{$email}:$last_changed";
		$invitation_id = wp_cache_get( $cache_key, $cache_group );
		if ( false === $invitation_id ) {
			global $wpdb;
			// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
			$invitation_id = $wpdb->get_var(
				$wpdb->prepare( "SELECT id FROM {$wpdb->prefix}gemboards_user_invitations WHERE object_id = %d AND email = %s", $project_id, $email )
			);
			// phpcs:enable
			$invitation_id = absint( $invitation_id );
			wp_cache_set( $cache_key, $invitation_id, $cache_group );
		}

		if ( ! $invitation_id ) {
			$invitation = new UserInvitation();
			$invitation->set_object_id( $project_id );
			$invitation->set_email( $email );
			$invitation->set_hash( Helper::generate_hash( 32 ) );
			$invitation->set_expired_at(
				gmdate( 'Y-m-d H:i:s', time() + DAY_IN_SECONDS * 7 )
			);
			$result = $invitation->save();
			if ( is_wp_error( $result ) ) {
				wp_send_json_error( [
					'message' => $result->get_error_message(),
				] );
			}
			wp_schedule_single_event(
				$invitation->get_date_expired()->getTimestamp(),
				'gemboards/schedule/expired_new_user_invitation',
				[ $invitation->get_id() ]
			);
		} else {
			$invitation = Helper::get_user_invitation( $invitation_id );
			if ( ! $invitation ) {
				wp_send_json_error( [
					'message' => __( 'Mismatch invitation id.', 'gemboards' ),
				] );
			}
		}

		$headers      = [ 'Content-Type: text/html; charset=UTF-8' ];
		$subject      = __( 'Invitation for joining project', 'gemboards' );
		$current_user = wp_get_current_user();
		$avatar_url   = get_avatar_url( get_current_user_id() );

		$invitation_link = add_query_arg( [
			'gemboards_invitation' => true,
			'hash'                 => $invitation->get_hash(),
		], home_url( '/' ) );

		$is_send = wp_mail( $email, wp_specialchars_decode( $subject ), Helper::get_email_body( 'new-user-invitation', [
			'avatar_url'        => $avatar_url,
			'user_display_name' => $current_user->display_name,
			'project_title'     => $project->get_title(),
			'invitation_link'   => $invitation_link,
		] ), $headers );

		if ( $is_send ) {
			$invitation->set_email_sent_count( $invitation->get_email_sent_count() + 1 );
			$invitation->save();
			wp_send_json_success( [ 'message' => __( 'Invitation sent successfully.', 'gemboards' ) ] );
		}

		wp_send_json_error( [ 'message' => __( 'Failed to send invitation.', 'gemboards' ) ] );
	}

	public function remove_new_user_invitation( array $payload ) {
		if ( empty( $payload['id'] ) ) {
			wp_send_json_error( [ 'message' => __( 'Invitation ID is required.', 'gemboards' ) ] );
		}

		if ( ! current_user_can( 'update_projects', [
			'invitation_id' => $payload['id'],
		] ) ) {
			wp_send_json_error( [ 'message' => __( 'You do not have permission to remove this user invitation.', 'gemboards' ) ] );
		}

		$invitation = Helper::get_user_invitation( $payload['id'] );
		if ( ! $invitation ) {
			wp_send_json_error( [ 'message' => __( 'Invalid invitation id.', 'gemboards' ) ] );
		}
		try {
			$invitation->delete();
			wp_send_json_success();
		} catch ( GemboardsException $e ) {
			Helper::error_log( $e->getMessage() );
			wp_send_json_error( [
				'message' => $e->get_wp_error()->get_error_message(),
			] );
		}
	}

	public function delete_project( $request ) {
		$id = $request['id'] ?? 0;
		if ( empty( $id ) ) {
			wp_send_json_error( [ 'message' => __( 'Project ID is required.', 'gemboards' ) ] );
		}

		$project = Helper::get_project( $id );
		if ( ! $project ) {
			wp_send_json_error( [
				'message' => __( 'Invalid project id.', 'gemboards' ),
			] );
		}

		try {
			$project->delete();

			global $wpdb;
			// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.DirectDatabaseQuery.NoCaching -- Clearing cach through entity class's `clear_cache` method and there is no unfinished prepare.
			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM {$wpdb->prefix}gemboards_roles WHERE object_id = %d AND object_type = 'project'", $id
				)
			);
			( new Role() )->clear_cache();

			$board_ids = ( new BoardCollection( [
				'per_page' => - 1,
				'fields'   => 'ids',
				'where'    => [
					[
						'key'   => 'project_id',
						'value' => $id,
					],
				],
			] ) )->get_results();
			$cards     = ( new CardCollection( [
				'per_page' => - 1,
				'where'    => [
					[
						'key'     => 'board_id',
						'value'   => $board_ids,
						'compare' => 'IN',
					],
				],
			] ) )->get_results();

			$task_ids            = array_map( fn( $card ) => $card->get_task_id(), $cards );
			$card_ids            = array_map( fn( $card ) => $card->get_id(), $cards );
			$board_ids_formatter = implode( ',', array_fill( 0, count( $board_ids ), '%d' ) );
			$card_ids_formatter  = implode( ',', array_fill( 0, count( $card_ids ), '%d' ) );
			$task_ids_formatter  = implode( ',', array_fill( 0, count( $task_ids ), '%d' ) );


			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM {$wpdb->prefix}gemboards_boards WHERE project_id = %d", $id
				)
			);
			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM {$wpdb->prefix}gemboards_folders WHERE project_id = %d", $id
				)
			);
			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM {$wpdb->prefix}gemboards_columns WHERE board_id IN ($board_ids_formatter)", ...$board_ids
				)
			);
			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM {$wpdb->prefix}gemboards_cards WHERE board_id IN ($board_ids_formatter)", ...$board_ids
				)
			);
			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM {$wpdb->prefix}gemboards_card_assignees WHERE card_id IN ($card_ids_formatter)", ...$card_ids
				)
			);
			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM {$wpdb->prefix}gemboards_pro_task_checklists WHERE card_id IN ($card_ids_formatter)", ...$card_ids
				)
			);
			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM {$wpdb->prefix}gemboards_pro_task_checklist_groups WHERE card_id IN ($card_ids_formatter)", ...$card_ids
				)
			);
			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM {$wpdb->prefix}gemboards_task_attachments WHERE task_id IN ($task_ids_formatter)", ...$task_ids
				)
			);
			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM {$wpdb->prefix}gemboards_notifications WHERE object_type = 'task' AND object_id IN ($task_ids_formatter)", ...$task_ids
				)
			);
			foreach ( $task_ids as $task_id ) {
				wp_delete_post( $task_id, true );
			}
			// phpcs:enable
			( new Role() )->clear_cache();
			( new \GemBoards\Entities\Board() )->clear_cache();
			( new \GemBoards\Entities\Folder() )->clear_cache();
			( new Column() )->clear_cache();
			( new \GemBoards\Entities\Card() )->clear_cache();
			( new CardAssignee() )->clear_cache();
			( new TaskChecklistGroup() )->clear_cache();
			( new TaskChecklist() )->clear_cache();
			( new TaskAttachment() )->clear_cache();
			( new Notification() )->clear_cache();


			wp_send_json_success( [ 'message' => __( 'Project deleted successfully.', 'gemboards' ) ] );
		} catch ( GemboardsException $e ) {
			wp_send_json_error( [ 'message' => $e->get_wp_error()->get_error_message() ] );
		}
	}

	private function get_project_data( \GemBoards\Entities\Project $project ): array {
		$data = $project->get_data();
		unset( $data['settings'] );

		return array_merge( $data, [
			'id'              => $project->get_id(),
			'background_type' => $project->get_background_type(),
			'background'      => $project->get_background(),
			'created_at'      => $project->get_date_created() ? $project->get_date_created()->format( 'Y-m-d H:i:s' ) : null,
			'updated_at'      => $project->get_date_updated() ? $project->get_date_updated()->format( 'Y-m-d H:i:s' ) : null,
		] );
	}
}
