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

use Academy\Classes\AbstractAjaxHandler;
use Academy\Classes\Sanitizer;
use Academy\Helper;
use AcademyProGroupPlus\Helper as GroupPlusHelper;
use AcademyProGroupPlus\Db\Models\{
    Group,
    Team
};
use AcademyProGroupPlus\Roles\{
    GroupOrganizer,
    TeamOrganizer,
    TeamMember
};
use WP_User;
use AcademyProGroupPlus\Interfaces\Ajax\Frontend\GroupHandlerInterface;

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


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

			'update_group' => array(
				'callback' => array( $this, 'update_group' ),
				'capability' => 'manage_academy_group',
			),
			'delete_group' => array(
				'callback' => array( $this, 'delete_group' ),
				'capability' => 'manage_academy_group',
			),

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

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

			'request_courses_to_group' => array(
				'callback' => array( $this, 'request_courses_to_group' ),
				'capability' => 'manage_academy_group',
			),
			'update_course_seats' => array(
				'callback' => array( $this, 'update_course_seats' ),
				'capability' => 'manage_academy_group',
			),
			'remove_courses_from_group' => array(
				'callback' => array( $this, 'remove_courses_from_group' ),
				'capability' => 'manage_academy_group',
			),
			'get_courses_from_group' => array(
				'callback' => array( $this, 'get_courses_from_group' ),
				'capability' => 'manage_academy_group',
			),
		);
	}

    public function get_groups( $payload_data ) : void
    {
		$payload = Sanitizer::sanitize_payload([

            'group_id'        => 'integer',

            'per_page'        => 'integer',
            'current_page'    => 'integer',
            'order_by'        => 'string',
            'search'          => 'string',
            'order_direction' => 'string',
        ], $payload_data);

        $conditions = [];
        $args = [];
        $user_id = get_current_user_id();
        $join = '';
        if(
            ! current_user_can('manage_options')
        ){
            $join  = "
                LEFT JOIN " . Group::add_prefix('group_organizers') . " ugr ON g.id = ugr.group_id
                LEFT JOIN " . Group::add_prefix('team_organizers')  . " tgr ON g.id = tgr.group_id
                LEFT JOIN " . Group::add_prefix('team_members')     . " tmr ON g.id = tmr.group_id
            ";
            
            $conditions[] = '(ugr.organizer_id = %d OR tgr.organizer_id = %d OR tmr.member_id = %d)';
            $args[]       = $user_id;
            $args[]       = $user_id;
            $args[]       = $user_id;
        }
        
        $group_id  = abs(intval($payload['group_id'] ?? 0));

        if($group_id > 0){
            $conditions[] = 'g.id = %d';
            $args[]       = $group_id;
            $payload['per_page'] = 1;
        }

        if(!empty($search = $payload['search'] ?? '')){
            $conditions[] = 'g.name LIKE %s';
            $args[]       = "%{$search}%";
        }
        
        $data = Group::ins()->get_advance(
            'g.*', // select rows
            'g.id', // select count for pagination
            "g", // main table alias
            $join,  // join clause
            $conditions, // where condition
            $args,   // where condition args
            ' GROUP BY g.id ',   // after where, before order & limit query
            $payload['per_page'] ?? 20, 
            $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'] );
                $group['is_current_user_leader'] = GroupPlusHelper::ins()->current_user_entity_exists(  $group['id'], 'group_organizers' );
                return $group;
            },
            $data['data'] ?? []
        );

        if ( isset( $data['id'] ) ) {
            $data['is_current_user_leader'] = GroupPlusHelper::ins()->current_user_entity_exists(  $data['id'], 'group_organizers' );
        }

        wp_send_json_success(
            $data,
            empty($data)&&($payload['per_page'] ?? 20) == 1?404:200
        );
    }

    public function get_group( $payload_data ) : void
    {
        $payload_data['per_page'] = 1;
        $this->get_groups($payload_data);
    }
    
    public function update_group( $payload_data ) : void
    {
		$payload = Sanitizer::sanitize_payload([
            'group_id'     => 'integer',
            'name'         => 'string',
            'description'  => 'string',
        ], $payload_data);

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

        /** VERIFY CURRENT USER IS ORGANIZER OF THIS GROUP OR NOT */
        if(!Group::ins()->is_organizer($group_id)){
            $output['message'] = __('Unauthorized.', 'academy-pro');
            wp_send_json_error($output, 401);
        }
       
        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, 404);
        }
 
		do_action( 'academy_pro_group_plus/api/before_update_group', $payload );
        $updated = Group::ins()->update([
            'name'         => $payload['name'],
            'description'  => $payload['description'] ?? $group['description'] ?? '',
        ],[
            'id' => $group_id /// where
        ]);

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

        $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);
    }

    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));

        /** VERIFY CURRENT USER IS ORGANIZER OF THIS GROUP OR NOT */
        if(!Group::ins()->is_organizer($group_id)){
            $output['message'] = __('Unauthorized.', 'academy-pro');
            wp_send_json_error($output, 401);
        }

        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( absint($group['user_id'] ?? '') !== get_current_user_id() ){
            $output['message'] = __( 'Only group creator can delete group.', '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
                );
            }
        }
        $output['id'] = $group_id;
        $output['message'] = __('Group Deleted!.', 'academy-pro');
        wp_send_json_success($output);
    }

    /** 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));


        /** VERIFY CURRENT USER IS ORGANIZER OF THIS GROUP OR NOT */
        if(!Group::ins()->is_organizer($group_id)){
            $output['message'] = __('Unauthorized.', 'academy-pro');
            wp_send_json_error($output, 401);
        }

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

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

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

        if(
            strlen($payload['password']) < 6
        ){
            $output['errors']['password'][] = __("password is too short, minimum password length is 10 characters.", 'academy-pro');
        }

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

        if(count($output['errors'] ?? [])){
            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);
        }


        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');
            $output['data'] = Group::get_user_info($user_id);
            wp_send_json_success($output, 201);
        }
        
        $output['message'] = __('Unknown error.', 'academy-pro');
        wp_send_json_error($output, 500);
    }
    /** organizer */
    public function get_selectable_organizers( $payload_data ) : void
    {
		$payload = Sanitizer::sanitize_payload([
            'group_id'  => 'integer',
            'email'     => 'string',
            'page'      => 'integer',
        ], $payload_data);

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

        /** VERIFY CURRENT USER IS ORGANIZER OF THIS GROUP OR NOT */
        if(!Group::ins()->is_organizer($group_id)){
            $output['message'] = __('Unauthorized.', 'academy-pro');
            wp_send_json_error($output, 401);
        }

        wp_send_json_success(
            Team::ins()
                ->selectable_organizers(
                    $group_id,
                    $payload['email'] ?? null,
                    $payload['page'] ?? 1,
                    20
                )
        );
    }
    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));
        

        /** VERIFY CURRENT USER IS ORGANIZER OF THIS GROUP OR NOT */
        if(!Group::ins()->is_organizer($group_id)){
            $output['message'] = __('Unauthorized.', 'academy-pro');
            wp_send_json_error($output, 401);
        }

        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['organigers'][] = Group::get_user_info( $organizer_id );
                $output['assigned'][] = $organizer_id;
            }
        }

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

    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));
        
        /** VERIFY CURRENT USER IS ORGANIZER OF THIS GROUP OR NOT */
        if(!Group::ins()->is_organizer($group_id)){
            $output['message'] = __('Unauthorized.', 'academy-pro');
            wp_send_json_error($output, 401);
        }

        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 removed organizers
        $output['message'] = sprintf( __( '%d organizer[s] removed.', 'academy-pro' ), count( $output['removed'] ?? [] ) );
        wp_send_json_success($output);
    } 

    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));

        
        /** VERIFY CURRENT USER IS ORGANIZER OF THIS GROUP OR NOT */
        if(!Group::ins()->is_organizer($group_id)){
            $output['message'] = __('Unauthorized.', 'academy-pro');
            wp_send_json_error($output, 401);
        }

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

    /** course */
    public function request_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));

        
        /** VERIFY CURRENT USER IS ORGANIZER OF THIS GROUP OR NOT */
        if(!Group::ins()->is_organizer($group_id)){
            $output['message'] = __('Unauthorized.', 'academy-pro');
            wp_send_json_error($output, 401);
        }

        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'] ?? false) || 
            !is_array($courses)
        ){
            $output['message'] = __('courses field should be filled with course_id.', '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

            ){
                $is_free = Helper::get_course_type($course_id) == 'free';
                $req     = $is_free ? false : true;
                if(Group::ins()->add_course($group_id, $course_id, $seats, $req)){
					$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: %1$d is the number of demanded seats, %2$d is the number of available seats
			$output['message'] = array_key_exists( 'demanded', $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'];
        // translators: %d is count 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);
    }
    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));

        /** VERIFY CURRENT USER IS ORGANIZER OF THIS GROUP OR NOT */
        if(!Group::ins()->is_organizer($group_id)){
            $output['message'] = __('Unauthorized.', 'academy-pro');
            wp_send_json_error($output, 401);
        }

        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_id.', '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

        ){
            $is_free = Helper::get_course_type($course_id) == 'free';
            $req     = $is_free ? false : true;
            if(Group::ins()->update_course($group_id, $course_id, $seats, $req)){
                wp_send_json_success([
                    'message'=> __('Course seats updated!', 'academy-pro')
                ]);
            }
        }
        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));


        /** VERIFY CURRENT USER IS ORGANIZER OF THIS GROUP OR NOT */
        if(!Group::ins()->is_organizer($group_id)){
            $output['message'] = __('Unauthorized.', 'academy-pro');
            wp_send_json_error($output, 401);
        }

        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_id.', '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 removed courses
        $output['message'] = sprintf( __( '%d course removed.', 'academy-pro' ), count($output['removed'] ?? []) );
        wp_send_json_success($output);
    }

    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));

        /** VERIFY CURRENT USER IS ORGANIZER OF THIS GROUP OR NOT */
        if(!Group::ins()->is_organizer($group_id)){
            $output['message'] = __('Unauthorized.', 'academy-pro');
            wp_send_json_error($output, 401);
        }

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