<?php

namespace HelpGent\App\Http\Controllers;


defined( 'ABSPATH' ) || exit;

use HelpGent\App\Models\Form;
use HelpGent\App\DTO\UserDTO;
use HelpGent\App\DTO\ChatMessageDTO;
use HelpGent\App\Repositories\ResponseRepository;
use HelpGent\App\Repositories\ChatMessageRepository;
use HelpGent\App\Http\Controllers\Controller;
use HelpGent\WpMVC\Exceptions\Exception;
use HelpGent\WpMVC\RequestValidator\Validator;
use HelpGent\WpMVC\Routing\Response;
use HelpGent\WpMVC\Database\Query\Builder;
use stdClass;
use WP_REST_Request;

class ChatMessageController extends Controller {
    public ResponseRepository $response_repository;

    public ChatMessageRepository $repository;

    public function __construct( ChatMessageRepository $repository, ResponseRepository $response_repository ) {
        $this->repository          = $repository;
        $this->response_repository = $response_repository;
    }

    public function index( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'response_id' => 'required|numeric',
                'per_page'    => 'numeric',
                'page'        => 'numeric',
                'search'      => 'string'
            ]
        );

        $user        = helpgent_get_current_user();
        $response_id = absint( $wp_rest_request->get_param( 'response_id' ) );
        
        $this->get_authorized_user_response( $response_id, $user, 'index_items' );

        return Response::send(
            [ 
                'messages' => $this->repository->get(
                    $response_id, 
                    absint( $wp_rest_request->get_param( 'per_page' ) ), 
                    absint( $wp_rest_request->get_param( 'page' ) ), 
                    (string) $wp_rest_request->get_param( 'search' ) 
                ) 
            ]
        );
    }

    public function store( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'response_id'      => 'required|numeric',
                'replied_by'       => 'required|string|max:50|accepted:video,voice,text,reply,screen_record',
                'message'          => 'string',
                'attachment_id'    => 'numeric',
                'attachment_ids'   => 'array',
                'parent_id'        => 'numeric|min:1',
                'notify_via_email' => 'numeric|accepted:0,1'
            ]
        );

        $user        = helpgent_get_current_user();
        $response_id = absint( $wp_rest_request->get_param( 'response_id' ) );
        
        $this->get_authorized_user_response( $response_id, $user, 'create_item' );

        /**
         * Requested message and attachment validation
         */
        $attachment_ids = (array) $wp_rest_request->get_param( 'attachment_ids' );
        $attachment_id  = intval( $wp_rest_request->get_param( 'attachment_id' ) );
        $message        = (string) $wp_rest_request->get_param( 'message' );
        
        if ( empty( $message ) && 0 >= $attachment_id && empty( $attachment_ids ) ) {
            $error = new Exception( '', 422 );
            
            $error->set_messages(
                [
                    'message' => [
                        esc_html__( "The message field is required", "helpgent" )
                    ]
                ]
            );

            throw $error;
        }

        $response = $this->response_repository->get_by_id( $response_id );

        if ( ! $response ) {
            throw new Exception( esc_html__( "Response not found", 'helpgent' ), 404 );
        }

        $columns = [ 'form.id', 'form.title', 'form.status', 'form.content', 'form.created_by' ];

        $form_query = Form::query( 'form' )->select( $columns )->where( 'form.id', $response->form_id )->group_by( $columns );

        /**
         * @var Builder $form_query
         */
        $form_query     = apply_filters( 'helpgent_chat_message_store_from_query', $form_query );
        $join_form_meta = apply_filters( 'helpgent_chat_message_store_from_query_join_meta', false );

        if ( $join_form_meta ) {
            $form_query->left_join( "helpgent_form_meta as form_meta", "form_meta.form_id", "=", "form.id" );
        }

        $form = helpgent_migrate_old_form( $form_query->first(), false );

        $message_dto = ( new ChatMessageDTO() )
            ->set_form_id( $form->id )
            ->set_response_id( $response_id )
            ->set_message( $message )
            ->set_created_by( $user->get_id() )
            ->set_attachment_id( $attachment_id )
            ->set_attachment_ids( $attachment_ids )
            ->set_parent_id( intval( $wp_rest_request->get_param( 'parent_id' ) ) )
            ->set_replied_by( $wp_rest_request->get_param( 'replied_by' ) );

        do_action( 'helpgent_before_store_chat_message', $message_dto, $wp_rest_request, $form, $response );
        
        $message_id = $this->repository->create( $message_dto );

        if ( $response->created_by === $user->get_id() ) {
            $this->response_repository->update_read( $response_id, 0 );
        } else {
            $this->response_repository->update_updated_at( $response_id );
        }

        $message_dto->set_id( $message_id );

        $message_data = $this->repository->get_by_id( $message_id );
        $message_data = $this->repository->prepare_message( $message_data );

        do_action( 'helpgent_after_store_chat_message', $message_dto, $wp_rest_request, $form, $response );
        
        return Response::send(
            [
                'message_data' => $message_data,
                'message'      => esc_html__( 'The message was created successfully', 'helpgent' )
            ], 
            201 
        );
    }

    public function update( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'id'          => 'required|numeric',
                'response_id' => 'required|numeric',
                'message'     => 'required|string'
            ]
        );

        $user        = helpgent_get_current_user();
        $response_id = absint( $wp_rest_request->get_param( 'response_id' ) );
        
        $this->get_authorized_user_response( $response_id, $user, 'update_item' );

        $message = $wp_rest_request->get_param( 'message' );

        $message_dto = ( new ChatMessageDTO() )
            ->set_id( intval( $wp_rest_request->get_param( 'id' ) ) )
            ->set_response_id( $response_id )
            ->set_message( $message );

        do_action( 'helpgent_before_update_chat_message', $message_dto, $wp_rest_request );

        $this->repository->update( $message_dto, $user->get_id() );

        do_action( 'helpgent_before_update_chat_message', $message_dto, $wp_rest_request );
        
        return Response::send( 
            [
                'message' => esc_html__( 'The message was updated successfully', 'helpgent' ) 
            ]
        );
    }

    public function bulk_read( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'ids'         => 'required|array',
                'response_id' => 'required|numeric',
                'is_read'     => 'required|accepted:0,1'
            ]
        );

        $user        = helpgent_get_current_user();
        $response_id = absint( $wp_rest_request->get_param( 'response_id' ) );
        
        $this->get_authorized_user_response( $response_id, $user, 'update_is_read' );

        $this->repository->update_bulk_read_status(
            $wp_rest_request->get_param( 'ids' ),
            $response_id,
            $wp_rest_request->get_param( 'is_read' )
        );
        
        return Response::send(
            [
                'message' => esc_html__( 'The read status was updated successfully', 'helpgent' )
            ] 
        );
    }

    public function admin_chat_unread_count() {
        return Response::send(
            [
                'count' => $this->repository->get_admin_unread_message_count(),
            ] 
        );
    }

    public function user_chat_unread_count( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'response_id' => 'required|string',
            ]
        );

        $user        = helpgent_get_current_user();
        $response_id = absint( $wp_rest_request->get_param( 'response_id' ) );
        $response    = $this->get_authorized_user_response( $response_id, $user );

        return Response::send(
            [
                'count' => $this->repository->get_response_unread_message_count( $response->created_by, $response->id ),
            ] 
        );
    }

    public function reviews( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'form_id'  => 'required|numeric',
                'type'     => 'string|accepted:good,bad',
                'page'     => 'numeric',
                'per_page' => 'numeric',
            ]
        );

        return Response::send(
            $this->repository->get_reviews(
                intval( $wp_rest_request->get_param( 'form_id' ) ),
                $wp_rest_request->get_param( 'type' ), 
                intval( $wp_rest_request->get_param( 'page' ) ), 
                intval( $wp_rest_request->get_param( 'per_page' ) )
            )
        );
    }

    public function attachment( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'type'           => 'required|string|accepted:all,image,audio,video,file',
                'response_id'    => 'numeric',
                'response_token' => 'string',
                'form_id'        => 'numeric',
            ]
        );

        $user        = helpgent_get_current_user();
        $response_id = absint( $wp_rest_request->get_param( 'response_id' ) );
        
        $this->get_authorized_user_response( $response_id, $user, 'index_attachments' );

        return Response::send(
            $this->repository->get_attachments(
                $response_id, 
                $wp_rest_request->get_param( 'type' ),
                intval( $wp_rest_request->get_param( 'per_page' ) ), 
                intval( $wp_rest_request->get_param( 'page' ) )
            )
        );
    }

    public function delete( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'id'          => 'numeric',
                'response_id' => 'required|numeric',
                'trash'       => 'accepted:0,1',
            ]
        );

        $user        = helpgent_get_current_user();
        $response_id = absint( $wp_rest_request->get_param( 'response_id' ) );

        $this->get_authorized_user_response( $response_id, $user, 'delete_item' );

        $trash = true;

        if ( $wp_rest_request->has_param( 'trash' ) ) {
            $trash = $wp_rest_request->get_param( 'trash' ) === '1';
        }

        $message = $this->repository->get_by_id( $wp_rest_request->get_param( 'id' ) );

        do_action( 'helpgent_before_delete_chat_message', $message, $wp_rest_request );

        if ( $trash ) {
            $this->repository->trash( intval( $wp_rest_request->get_param( 'id' ) ), $response_id, $user ? $user->get_id() : 0 );
        } else {
            $this->repository->delete( intval( $wp_rest_request->get_param( 'id' ) ), $response_id, $user ? $user->get_id() : 0 );
        }

        do_action( 'helpgent_after_delete_chat_message', $message, $wp_rest_request );

        if ( $trash ) {
            $message = esc_html__( 'The message was moved to trash successfully', 'helpgent' );
        } else {
            $message = esc_html__( 'The message was deleted successfully', 'helpgent' );
        }

        return Response::send( 
            [
                'message' => $message
            ]
        );
    }

    public function restore( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'id'          => 'numeric',
                'response_id' => 'required|numeric',
            ]
        );

        $user        = helpgent_get_current_user();
        $response_id = absint( $wp_rest_request->get_param( 'response_id' ) );

        $this->get_authorized_user_response( $response_id, $user, 'restore_item' );

        $message = $this->repository->get_by_id( $wp_rest_request->get_param( 'id' ) );

        do_action( 'helpgent_before_restore_chat_message', $message, $wp_rest_request );

        $this->repository->restore( intval( $wp_rest_request->get_param( 'id' ) ), $response_id, $user ? $user->get_id() : 0 );
        
        do_action( 'helpgent_after_restore_chat_message', $message, $wp_rest_request );

        return Response::send(
            [
                'message' => esc_html__( 'The message was restored successfully', 'helpgent' )
            ]
        );
    }

    public function get_authorized_user_response( int $response_id, ?UserDTO $user, string $context = 'index' ) {
        $response = $this->response_repository->get_by_id( $response_id );

        if ( empty( $response ) ) {
            throw new Exception( esc_html__( "Response not found", 'helpgent' ), 404 );
        }

        if ( ! $this->is_user_allowed_to_access_response( $response, $user, $context ) ) {
            throw new Exception( esc_html__( "Sorry, access to this response's conversations is not allowed.", 'helpgent' ), 401 );
        }

        return $response;
    }

    public function is_user_allowed_to_access_response( stdClass $response, ?UserDTO $user, string $context = '' ): bool {
        $is_user_allowed = $this->can_access_response( $response,$user );

        return apply_filters( 'helpgent_is_user_allowed_response_chat_message', $is_user_allowed, $response, $user, $context );
    }

    public function can_access_response( stdClass $response, ?UserDTO $user ): bool {
        if ( ! $user ) {
            return false;
        }

        if ( $user->is_administrator() || $user->is_agent() ) {
            return true;
        }

        return absint( $response->created_by ) === $user->get_id();
    }
}