<?php
namespace AcademyProGroupPlus\Dashboard\Ajax\Admin;

use Academy\Classes\AbstractAjaxHandler;
use Academy\Classes\Sanitizer;
use AcademyProGroupPlus\Db\Models\Group;
use Academy\Helper;
use AcademyProGroupPlus\Roles\{
	GroupOrganizer,
	TeamOrganizer
};
use WP_User;
use AcademyProGroupPlus\interfaces\Ajax\Admin\GroupHandlerInterface;

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


class GroupHandler extends AbstractAjaxHandler implements GroupHandlerInterface {
	protected $namespace = 'academy_pro_group_plus';
	public function __construct() {
		$this->actions = array(
			'get_group' => array(
				'callback' => array( $this, 'get_group' ),
			),
			'get_groups' => array(
				'callback' => array( $this, 'get_groups' ),
			),

			'create_group' => array(
				'callback' => array( $this, 'create_group' ),
			),
			'duplicate_group' => array(
				'callback' => array( $this, 'duplicate_group' ),
			),
			'update_group' => array(
				'callback' => array( $this, 'update_group' ),
			),
			'delete_group' => array(
				'callback' => array( $this, 'delete_group' ),
			),

			'create_organizer' => array(
				'callback' => array( $this, 'create_organizer' ),
			),

			'add_organizers_to_group' => array(
				'callback' => array( $this, 'add_organizers_to_group' ),
			),
			'remove_organizers_from_group' => array(
				'callback' => array( $this, 'remove_organizers_from_group' ),
			),
			'get_organizers_from_group' => array(
				'callback' => array( $this, 'get_organizers_from_group' ),
			),

			'add_courses_to_group' => array(
				'callback' => array( $this, 'add_courses_to_group' ),
			),
			'update_course_seats' => array(
				'callback' => array( $this, 'update_course_seats' ),
			),
			'remove_courses_from_group' => array(
				'callback' => array( $this, 'remove_courses_from_group' ),
			),
			'get_courses_from_group' => array(
				'callback' => array( $this, 'get_courses_from_group' ),
			),
		);
	}

	public function get_groups( $payload_data ) : void {
		$payload = Sanitizer::sanitize_payload([
			'per_page'        => 'integer',
			'current_page'    => 'integer',
			'order_by'        => 'string',
			'search'          => 'string',
			'order_direction' => 'string',
		], $payload_data);

		$conditions = [];
		$args = [];

		if ( ! empty( $search = $payload['search'] ?? '' ) ) {
			$conditions[] = 'name LIKE %s';
			$args[]       = "%{$search}%";
		}
		$per_page = $payload['per_page'] ?? 20;

		$data = Group::ins()->get(
			$conditions,
			$args,
			( $per_page > 100 ? 100 : $per_page ),
			$payload['current_page'] ?? 1,
			$payload['order_by'] ?? null,
			$payload['order_direction'] ?? 'DESC'
		);

		$data['data'] = array_map(
			function( array $group ) {
				$group['author'] = Group::get_user_info( $group['user_id'] );
				return $group;
			},
			$data['data'] ?? []
		);

		wp_send_json_success(
			$data,
			empty( $data ) ? 204 : 200
		);
	}

	public function get_group( $payload_data ) : void {
		$payload = Sanitizer::sanitize_payload([
			'group_id'        => 'integer',
		], $payload_data);

		$output = [];
		$group_id = abs( intval( $payload['group_id'] ?? 0 ) );
		if (
			( $group_id == 0 ) ||
			empty( $group = Group::ins()->get_by_id( $group_id ) )
		) {
			$output['message'] = __( 'Invalid Group ID.', 'academy-pro' );
			wp_send_json_error( $output, 404 );
		}
		$group['author'] = Group::get_user_info( $group['user_id'] );
		wp_send_json_success( $group, 200 );

	}

	public function create_group( $payload_data ) : void {
		$payload = Sanitizer::sanitize_payload([
			'name'         => 'string',
			'description'  => 'string',
			'team_limit'   => 'integer',
			'member_limit' => 'integer',
			'is_prebuilt'  => 'integer', // 0 or 1
		], $payload_data);

		$output = [];

		if ( empty( $payload['name'] ?? '' ) ) {
			$output['message'] = __( 'Group name is required.', 'academy-pro' );
			wp_send_json_error( $output, 400 );
		}
		$data = [
			'name'         => $payload['name'],
			'description'  => $payload['description'] ?? '',
			'team_limit'   => $payload['team_limit'] ?? 100,
			'member_limit' => $payload['member_limit'] ?? 500,
			'is_prebuilt'  => $payload['is_prebuilt'] ?? 0, // 0 or 1
			'user_id'      => get_current_user_id(),
		];
		do_action( 'academy_pro_group_plus/api/before_create_group', $data );
		$group_id = Group::ins()->create( $data );

		if ( ! $group_id ) {
			do_action( 'academy_pro_group_plus/api/after_create_group', $data );
			$output['message'] = __( 'Error.', 'academy-pro' );
			wp_send_json_error( $output, 500 );
		}

		$output['message'] = __( 'Group created!.', 'academy-pro' );
		$output['data'] = Group::ins()->get_by_id( $group_id );
		$output['data']['author'] = Group::get_user_info( $output['data']['user_id'] ?? [] );
		wp_send_json_success( $output, 201 );
	}

	public function duplicate_group( $payload_data ) : void {
		$payload = Sanitizer::sanitize_payload([
			'group_id' => 'integer',
		], $payload_data);

		$output = [];

		$group_id = abs( intval( $payload['group_id'] ?? 0 ) );
		if ( $group_id === 0 || empty( $group = Group::ins()->get_by_id( $group_id ) ) ) {
			$output['message'] = __( 'Invalid Group ID.', 'academy-pro' );
			wp_send_json_error( $output, 404 );
		}

		$data = [
			'name'         => $group['name'] . ' (copy)',
			'description'  => $group['description'] ?? '',
			'team_limit'   => $group['team_limit'] ?? 100,
			'member_limit' => $group['member_limit'] ?? 500,
			'is_prebuilt'  => $group['is_prebuilt'] ?? 0, // 0 or 1
			'user_id'      => get_current_user_id(),
		];

		$group_id = Group::ins()->create( $data );

		if ( ! $group_id ) {
			$output['message'] = __( 'Error duplicating group.', 'academy-pro' );
			wp_send_json_error( $output, 500 );
		}

		$output['message'] = __( 'Group duplicated successfully!', 'academy-pro' );
		$output['data'] = Group::ins()->get_by_id( $group_id );
		$output['data']['author'] = Group::get_user_info( $output['data']['user_id'] ?? [] );
		$output['id'] = $group_id;
		wp_send_json_success( $output, 201 );
	}

	public function update_group( $payload_data ) : void {
		$payload = Sanitizer::sanitize_payload([
			'group_id'     => 'integer',
			'name'         => 'string',
			'description'  => 'string',
			'team_limit'   => 'integer',
			'member_limit' => 'integer',
			'is_prebuilt'  => 'integer', // 0 or 1
		], $payload_data);

		$output = [];
		$group_id = abs( intval( $payload['group_id'] ?? 0 ) );
		if (
			( $group_id == 0 ) ||
			empty( $group = Group::ins()->get_by_id( $group_id ) )
		) {
			$output['message'] = __( 'Invalid Group ID.', 'academy-pro' );
			wp_send_json_error( $output, 404 );
		}

		if ( empty( $payload['name'] ?? '' ) ) {
			$output['message'] = __( 'Group name is required.', 'academy-pro' );
			wp_send_json_error( $output, 400 );
		}

		$data = [
			'name'         => $payload['name'],
			'description'  => $payload['description'] ?? $group['description'] ?? '',
			'team_limit'   => $payload['team_limit'] ?? $group['team_limit'] ?? 100,
			'member_limit' => $payload['member_limit'] ?? $group['member_limit'] ?? 500,
			'is_prebuilt'  => $payload['is_prebuilt'] ?? $group['is_prebuilt'] ?? 0, // 0 or 1
		];

		do_action( 'academy_pro_group_plus/api/before_update_group', $payload );

		$updated = Group::ins()->update($data, [
			'id' => $group_id // where
		]);

		if ( ! $updated ) {
			$output['message'] = __( 'Error.', 'academy-pro' );
			wp_send_json_error( $output, 500 );
		}

		do_action( 'academy_pro_group_plus/api/after_update_group', $payload );

		$output['data'] = Group::ins()->get_by_id( $group_id );
		$output['data']['author'] = Group::get_user_info( $output['data']['user_id'] ?? [] );
		$output['message'] = __( 'Group Updated!.', 'academy-pro' );
		wp_send_json_success( $output, 200 );
	}

	public function delete_group( $payload_data ) : void {
		$payload = Sanitizer::sanitize_payload([
			'group_id'     => 'integer',
		], $payload_data);

		$output = [];

		$group_id = abs( intval( $payload['group_id'] ?? 0 ) );

		if (
			( $group_id == 0 ) ||
			empty( $group = Group::ins()->get_by_id( $group_id ) )
		) {
			$output['message'] = __( 'Invalid Group ID.', 'academy-pro' );
			wp_send_json_error( $output, 404 );
		}

		do_action( 'academy_pro_group_plus/api/before_delete_group', $group_id );
		$deleted = Group::ins()->delete([
			'id' => $group_id // where
		]);

		if ( ! $deleted ) {
			do_action( 'academy_pro_group_plus/api/after_delete_group', $group_id );
			$output['message'] = __( 'Error.', 'academy-pro' );
			wp_send_json_error( $output, 500 );
		} else {
			global $wpdb;
			$where = [
				'group_id' => $group_id
			];
			$tables = [
				'academy_group_courses',
				'academy_group_organizers',
				'academy_teams',
				'academy_team_courses',
				'academy_team_organizers',
				'academy_team_members',
			];
			foreach ( $tables as $tables ) {
				// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
				$wpdb->delete(
					$wpdb->prefix . $tables,
					$where
				);
			}
		}//end if

		$output['id'] = $group_id;
		$output['message'] = __( 'Group Deleted!.', 'academy-pro' );
		wp_send_json_success( $output, 200 );
	}

	/** create organizer */
	public function create_organizer( $payload_data ) : void {
		$payload = Sanitizer::sanitize_payload([
			'group_id'        => 'integer',

			'username'        => 'string',
			'email'           => 'string',
			'password'        => 'string',
		], $payload_data);

		$output = [];

		$group_id  = abs( intval( $payload['group_id'] ?? 0 ) );

		if (
			( $group_id == 0 ) ||
			empty( Group::ins()->get_by_id( $group_id ) )
		) {
			$output['message'] = __( 'Invalid Group ID.', 'academy-pro' );
			wp_send_json_error( $output, 404 );
		}

		if ( empty( $payload['username'] ?? '' ) ) {
			$output['errors'][] = __( 'username field is required.', 'academy-pro' );
		}
		if ( empty( $payload['email'] ?? '' ) ) {
			$output['errors'][] = __( 'email field is required.', 'academy-pro' );
		}
		if ( empty( $payload['password'] ?? '' ) ) {
			$output['errors'][] = __( 'password field is required.', 'academy-pro' );

		}

		if (
			! empty( $payload['username'] ?? '' ) &&
			username_exists( $payload['username'] )
		) {
			$output['errors'][] = __( 'username  already exists.', 'academy-pro' );
		}

		if (
			! empty( $payload['email'] ?? '' ) &&
			email_exists( $payload['email'] )
		) {
			$output['errors'][] = __( 'email already exists.', 'academy-pro' );
		}

		if (
			strlen( $payload['password'] ) > 30
		) {
			$output['errors'][] = __( 'password is too long, maximum password length is 30 characters.', 'academy-pro' );
		}

		if ( count( $output['errors'] ?? [] ) > 0 ) {
			wp_send_json_error( $output, 400 );
		}

		do_action( 'academy_pro_group_plus/api/before_create_group_organizer', $payload );
		$user_id = wp_create_user( $payload['username'], $payload['password'], $payload['email'] );

		if ( is_wp_error( $user_id ) ) {
			$output['message'] = __( 'Error [1].', 'academy-pro' );
			wp_send_json_error( $output, 500 );
		}

		$output['data'] = Group::get_user_info( $user_id );
		do_action( 'academy_pro_group_plus/api/after_create_group_organizer', $payload );

		if ( Group::ins()->add_organizer( $group_id, $user_id ) ) {
			$output['message'] = __( 'Organizer created & assigned to group!', 'academy-pro' );
			wp_send_json_success( $output, 201 );
		}

		$output['message'] = __( 'Unknown error.', 'academy-pro' );
		wp_send_json_error( $output, 500 );
	}
	/** organizer */
	public function add_organizers_to_group( $payload_data ) : void {
		$payload = Sanitizer::sanitize_payload([
			'group_id'     => 'integer',
			'organizers'   => 'array',
		], $payload_data);

		$output = [];
		$group_id = abs( intval( $payload['group_id'] ?? 0 ) );
		if (
			( $group_id == 0 ) ||
			empty( $group = Group::ins()->get_by_id( $group_id ) )
		) {
			$output['message'] = __( 'Invalid Group ID.', 'academy-pro' );
			wp_send_json_error( $output, 404 );
		}

		if (
			empty( $organizers = $payload['organizers'] ?? false ) ||
			! is_array( $organizers )
		) {
			$output['message'] = __( 'organizers field should be filled with organizer_id.', 'academy-pro' );
			wp_send_json_error( $output, 400 );
		}

		foreach ( $organizers as $organizer_id ) {
			if ( Group::ins()->add_organizer( $group_id, $organizer_id ) ) {
				$output['assigned'][] = $organizer_id;
				$output['organigers'][] = Group::get_user_info( $organizer_id );
			}
		}

		$output['data'] = $output['organigers'][0] ?? [];
		// translators: %d is count organizers
		$output['message'] = sprintf( __( '%d organizer[s] assigned.', 'academy-pro' ), count( $output['assigned'] ?? [] ) );
		wp_send_json_success( $output, 200 );
	}

	public function remove_organizers_from_group( $payload_data ) : void {
		$payload = Sanitizer::sanitize_payload([
			'group_id'     => 'integer',
			'organizers'   => 'array',
		], $payload_data);

		$output = [];
		$group_id = abs( intval( $payload['group_id'] ?? 0 ) );
		if (
			( $group_id == 0 ) ||
			empty( $group = Group::ins()->get_by_id( $group_id ) )
		) {
			$output['message'] = __( 'Invalid Group ID.', 'academy-pro' );
			wp_send_json_error( $output, 404 );
		}

		if (
			empty( $organizers = $payload['organizers'] ?? false ) ||
			! is_array( $organizers )
		) {
			$output['message'] = __( 'organizers field should be filled with organizer_id.', 'academy-pro' );
			wp_send_json_error( $output, 400 );
		}

		foreach ( $organizers as $organizer_id ) {
			if ( Group::ins()->remove_organizer( $group_id, $organizer_id ) ) {
				$output['removed'][] = $organizer_id;
			}
		}

		$output['id'] = $output['removed'][0] ?? 0;
		// translators: %d is count remove organizers
		$output['message'] = sprintf( __( '%d organizer[s] removed.', 'academy-pro' ), count( $output['removed'] ?? [] ) );
		wp_send_json_success( $output, 200 );
	}

	public function get_organizers_from_group( $payload_data ) : void {
		$payload = Sanitizer::sanitize_payload([
			'group_id'     => 'integer',

			'per_page'     => 'integer',
			'current_page' => 'integer',
		], $payload_data);

		$group_id = abs( intval( $payload['group_id'] ?? 0 ) );

		wp_send_json_success(
			Group::ins()->organizers(
				$group_id,
				abs( intval( $payload['per_page'] ?? 20 ) ),
				abs( intval( $payload['current_page'] ?? 1 ) )
			),
			200
		);
	}

	/** course */
	public function add_courses_to_group( $payload_data ) : void {
		$payload = Sanitizer::sanitize_payload([
			'group_id'     => 'integer',
			'courses'      => 'array', // course ID => seats
		], $payload_data);

		$output = [];
		$group_id = abs( intval( $payload['group_id'] ?? 0 ) );
		if (
			( $group_id == 0 ) ||
			empty( $group = Group::ins()->get_by_id( $group_id ) )
			) {
				$output['message'] = __( 'Invalid Group ID.', 'academy-pro' );
				wp_send_json_error( $output, 404 );
		}

		if (
				empty( $courses = (array) $payload['courses'] ?? [] ) ||
				! is_array( $courses )
				) {
			$output['message'] = __( 'courses field should be filled with course ids.', 'academy-pro' );
			wp_send_json_error( $output, 400 );
		}
		foreach ( $courses as $course_id => $seats ) {

			$available_seats = Helper::get_available_seats( $course_id );
			if (
				$available_seats > 0 &&
				$seats > 0 &&
				$available_seats >= $seats

			) {
				if ( Group::ins()->add_course( $group_id, $course_id, $seats ) ) {
					$output['assigned'][ $course_id ] = $seats;
				}
			} else {
				$output['errors'][ $course_id ] = [
					'available_seats' => $available_seats,
					'demanded'        => $seats,
				];
			}
		}

		if ( ( $total = count( $output['assigned'] ?? [] ) ) === 0 ) {
			$error = current( $output['errors'] ?? [] );
			$demanded = $error['demanded'] ?? 0;
			$available = $error['available_seats'] ?? 0;

			// translators: %d is the number of demanded seats, %d is the number of available seats
			$output['message'] = array_key_exists( 'demanded', (array) $error ) ? sprintf( __( 'you have demanded %1$d, but available seats are %2$d.', 'academy-pro' ), $demanded, $available ) : __( 'No course assigned.', 'academy-pro' );
			wp_send_json_error( $output, 400 );
		}

		$output['total'] = $total;
		$output['group_id'] = $group_id;
		$output['courses'] = $output['assigned'];
		$output['seats'] = $output['assigned'];
		// translators: %d is total assigned courses
		$output['message'] = sprintf( __( ' %d course(s) assigned.', 'academy-pro' ), $total );

		global $wpdb;
		$output['assigned'] = array_map(
			fn ( array $course ) : array => array_merge( [ 'seats' => $output['courses'][ $course['ID'] ] ?? null ], $course ),
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->get_results(
				$wpdb->prepare(
					"SELECT ID, post_title, post_status 
					FROM {$wpdb->posts}
					WHERE ID IN (" . implode( ', ', array_fill( 0, $total, '%d' ) ) . ')', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
					...array_keys( $output['assigned'] )
				),
				ARRAY_A
			) ?? []
		);

		wp_send_json_success( $output, 200 );
	}

	public function update_course_seats( $payload_data ) : void {
		$payload = Sanitizer::sanitize_payload([
			'group_id'     => 'integer',
			'course_id'    => 'integer',
			'seats'        => 'integer',
		], $payload_data);

		$output = [];
		$group_id  = abs( intval( $payload['group_id'] ?? 0 ) );
		$course_id = abs( intval( $payload['course_id'] ?? 0 ) );
		$seats     = abs( intval( $payload['seats'] ?? 0 ) );

		if (
			( $group_id == 0 ) ||
			empty( $group = Group::ins()->get_by_id( $group_id ) )
		) {
			$output['message'] = __( 'Invalid Group ID.', 'academy-pro' );
			wp_send_json_error( $output, 404 );
		}

		if (
			empty( $course_id )
		) {
			$output['message'] = __( 'courses field should be filled with course ids.', 'academy-pro' );
			wp_send_json_error( $output, 400 );
		}

		$available_seats = Helper::get_available_seats( $course_id );
		if (
			$available_seats > 0 &&
			$seats > 0 &&
			$available_seats >= $seats

		) {
			if ( Group::ins()->update_course( $group_id, $course_id, $seats ) ) {
				wp_send_json_success([
					'message' => __( 'Course seats updated!', 'academy-pro' )
				], 200);
			}
		}
		wp_send_json_error([
			'message' => __( 'Error.', 'academy-pro' ),
			'available_seats' => $available_seats,
			'demanded'        => $seats,
		], 400);
	}

	public function remove_courses_from_group( $payload_data ) : void {
		$payload = Sanitizer::sanitize_payload([
			'group_id'     => 'integer',
			'courses'      => 'array',
		], $payload_data);

		$output = [];
		$group_id = abs( intval( $payload['group_id'] ?? 0 ) );
		if (
			( $group_id == 0 ) ||
			empty( $group = Group::ins()->get_by_id( $group_id ) )
		) {
			$output['message'] = __( 'Invalid Group ID.', 'academy-pro' );
			wp_send_json_error( $output, 404 );
		}

		if (
			empty( $courses = $payload['courses'] ?? false ) ||
			! is_array( $courses )
		) {
			$output['message'] = __( 'courses field should be filled with course ids.', 'academy-pro' );
			wp_send_json_error( $output, 400 );
		}

		foreach ( $courses as $course_id ) {
			if ( Group::ins()->remove_course( $group_id, $course_id ) ) {
				$output['removed'][] = $course_id;
			}
		}
		$output['id'] = $output['removed'][0] ?? 0;
		// translators: %d is count remove courses
		$output['message'] = sprintf( __( '%d course removed.', 'academy-pro' ), count( $output['removed'] ?? [] ) );
		wp_send_json_success( $output, 200 );
	}

	public function get_courses_from_group( $payload_data ) : void {
		$payload = Sanitizer::sanitize_payload([
			'group_id'     => 'integer',

			'per_page'     => 'integer',
			'current_page' => 'integer',
		], $payload_data);

		$group_id = abs( intval( $payload['group_id'] ?? 0 ) );

		wp_send_json_success(
			Group::ins()->courses(
				$group_id,
				abs( intval( $payload['per_page'] ?? 20 ) ),
				abs( intval( $payload['current_page'] ?? 1 ) )
			),
			200
		);
	}
}
