<?php

namespace HelpGent\App\Http\Controllers;

defined( 'ABSPATH' ) || exit;

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

class MessageController extends Controller {
    public ResponseRepository $response_repository;

    public MessageRepository $repository;

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

    public function is_auth_user( UserDTO $user ) {
        return $user->is_administrator() || $user->is_agent();
    }

    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     = intval( $wp_rest_request->get_param( 'response_id' ) );
        $this->get_authorized_user_response( $response_id, $user, 'index_items' );

        return Response::send(
            $this->repository->get( 
                $response_id, 
                intval( $wp_rest_request->get_param( 'per_page' ) ), 
                intval( $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' => 'integer',
                'parent_id'     => 'integer|min:1'
            ]
        );

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

            throw $error;
        }

        $user = helpgent_get_current_user();

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

        $form = $this->get_form( $response->form_id );

        $message_dto = ( new MessageDTO() )
            ->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_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_message', $message_dto, $wp_rest_request, $form, $response );
        
        $message_id = $this->repository->create( $message_dto );

        $this->response_repository->update_updated_at( $response_id );

        if ( ! $this->is_auth_user( $user ) ) {
            $this->response_repository->update_user_activity( $response_id );
        }

        $message_dto->set_id( $message_id );

        do_action( 'helpgent_after_store_message', $message_dto, $wp_rest_request, $form, $response );
        
        return Response::send( [], 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 = intval( $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 MessageDTO() )->set_id( intval( $wp_rest_request->get_param( 'id' ) ) )
            ->set_response_id( $response_id )
            ->set_message( $message );

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

        $this->repository->update( $message_dto );

        do_action( 'helpgent_before_update_message', $message_dto, $wp_rest_request );
        
        return Response::send( [] );
    }

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

        $this->repository->update_read_status(
            absint( $wp_rest_request->get_param( 'id' ) ),
            absint( $wp_rest_request->get_param( 'response_id' ) ),
            $wp_rest_request->get_param( 'is_read' )
        );
        
        return Response::send( [] );
    }

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

        $this->repository->update_bulk_read_status(
            $wp_rest_request->get_param( 'ids' ),
            absint( $wp_rest_request->get_param( 'response_id' ) ),
            $wp_rest_request->get_param( 'is_read' )
        );
        
        return Response::send( [] );
    }

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

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

        $this->repository->delete( intval( $wp_rest_request->get_param( 'id' ) ), $response_id );

        return Response::send( [] );
    }

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

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

        return Response::send(
            $this->repository->attachment( 
                $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 get_by_id( int $id ) {
        return Message::query()->where( 'id', $id )->first();
    }

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

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

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

        if ( $this->requires_replyable_response( $context ) && ! $this->can_store_message_for_response( $response, $user, $context ) ) {
            throw new Exception( esc_html__( "Replies are not allowed for this response.", 'helpgent' ), 403 );
        }

        return $response;
    }

    protected function has_response_access( stdClass $response, ?UserDTO $user, string $context ): bool {
        $is_user_allowed = apply_filters(
            'helpgent_is_user_allowed_response_message',
            false,
            $response->id,
            $response,
            $user,
            $context
        );

        if ( $is_user_allowed ) {
            return true;
        }

        return $this->can_access_response( $response, $user );
    }

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

        if ( $this->is_auth_user( $user ) ) {
            return true;
        }

        return (bool) $this->response_repository->first_by_created_by(
            absint( $response->id ),
            $user->get_id()
        );
    }

    protected function can_store_message_for_response( stdClass $response, ?UserDTO $user, string $context ): bool {
        return (bool) apply_filters(
            'helpgent_can_store_response_message',
            $this->response_repository->can_user_store_message( $response, $user ),
            $response,
            $user,
            $context
        );
    }

    protected function requires_replyable_response( string $context ): bool {
        return in_array( $context, [ 'create_item', 'upload_attachment', 'create_upload_attachment' ], true );
    }

    public function get_form( int $form_id ): stdClass {
        $columns = ['form.id', 'form.title', 'form.status', 'form.content', 'form.created_by'];

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

        /**
         * @var Builder $form_query
         */
        $form_query     = apply_filters( 'helpgent_message_store_from_query', $form_query );
        $join_form_meta = apply_filters( 'helpgent_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 = $form_query->first();

        if ( ! $form ) {
            throw new Exception( esc_html__( "Form not found.", "helpgent" ) );
        }

        $form          = helpgent_migrate_old_form( $form );
        $form->content = json_decode( $form->content, true );

        return $form;
    }
}
