<?php

namespace HelpGent\App\Repositories;

defined( 'ABSPATH' ) || exit;

use stdClass;
use Exception;

use HelpGent\App\Utils\Repository;
use HelpGent\App\DTO\ChatMessageDTO;
use HelpGent\App\Models\ChatMessage;
use HelpGent\App\Models\HelpGentUser;
use HelpGent\App\Models\UserMeta;
use HelpGent\App\Models\ElementAnswers;
use HelpGent\App\Models\Response;
use HelpGent\App\Models\Review;
use HelpGent\App\Models\Attachment;

use HelpGent\App\Repositories\ResponseRepository;
use HelpGent\App\Repositories\AttachmentRepository;
use HelpGent\App\Support\Element\Fields\LiveChat;
use HelpGent\WpMVC\Database\Query\Builder;
use HelpGent\WpMVC\Database\Query\JoinClause;

class ChatMessageRepository {
    public ResponseRepository $response_repository;

    public AttachmentRepository $attachment_repository;

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

    public function get( int $response_id, int $per_page, int $page, string $search = '' ) {
        $chat_table = ChatMessage::get_table_name();
        $query      = ChatMessage::query()->where( "{$chat_table}.response_id", $response_id );

        if ( ! empty( $search ) ) {
            $query->where_like( "{$chat_table}.message", "%{$search}%" );
        }

        $query->select( $this->select_columns() )->with( $this->chat_message_relations() )->left_join(
            HelpGentUser::get_table_name() . ' as user', function( $join ) use( $chat_table ) {
                $join->on_column( "{$chat_table}.created_by", 'user.id' );
            }
        )->order_by_desc( "{$chat_table}.id" );
        
        if ( -1 === $per_page ) {
            $messages = $query->get();
        } else {
            $messages = $query->pagination( $page, $per_page );
        }

        if ( ! is_array( $messages ) ) {
            return [];
        }

        return array_map( [ $this, 'prepare_message' ] , $messages );
    }

    public function member_has_message_in_response( int $member_id, int $response_id ): bool {
        $message = ChatMessage::query()
            ->where( 'created_by', $member_id )
            ->where( 'response_id', $response_id )
            ->first();

        return ! empty( $message );
    }

    public function get_by_id( int $id ) {
        $chat_table = ChatMessage::get_table_name();
        $item       = ChatMessage::query()
            ->select( $this->select_columns() )
            ->with( $this->chat_message_relations() )
            ->left_join(
                HelpGentUser::get_table_name() . ' as user', function( $join ) use( $chat_table ) {
                    $join->on_column( "{$chat_table}.created_by", 'user.id' );
                }
            )
            ->where( "{$chat_table}.id", $id )
            ->first();

        if ( empty( $item ) ) {
            throw new Exception( esc_html__( 'The item was not found', 'helpgent' ), 404 );
        }

        return $item;
    }

    protected function chat_message_relations() {
        return [
            'parent'            => function( Builder $query ) {
                $query->select( $this->select_columns() )->left_join(
                    HelpGentUser::get_table_name() . ' as user', function( $join ) {
                        $chat_table = ChatMessage::get_table_name();
                        $join->on_column( "{$chat_table}.created_by", 'user.id' );
                    }
                );
            },
            'parent.attachment' => [ Repository::class, 'attachment_select_query' ],
            'attachment'        => [ Repository::class, 'attachment_select_query' ],
        ];
    }

    public static function attachment_select_query( $query ) {
        return $query->select( 'id, title, mime_type, file_size, file_id, storage' );
    }

    protected function select_columns() {
        $chat_table = ChatMessage::get_table_name();

        return [
            "{$chat_table}.id",
            "{$chat_table}.response_id",
            "{$chat_table}.attachment_id",
            "{$chat_table}.attachment_ids",
            "{$chat_table}.message",
            "{$chat_table}.is_read",
            "{$chat_table}.created_by",
            "{$chat_table}.replied_by",
            "{$chat_table}.parent_id",
            "{$chat_table}.status",
            "{$chat_table}.updated_at",
            "{$chat_table}.created_at",
            "user.id as user_id", 
            "user.user_type as user_type", 
            "user.name as user_name", 
            "user.email as user_email",
        ];
    }

    public function prepare_message( stdClass &$message ) {
        $message->is_admin_message = false;
        $message->is_agent_message = false;

        if ( ! empty( $message->user_email ) ) {
            $message->user_avatar_url = get_avatar_url( $message->user_email );

            $user = get_user_by( 'email', $message->user_email );

            if ( $user ) {
                if ( apply_filters( 'helpgent_is_user_administrator', in_array( 'administrator', $user->roles ) || user_can( $user, 'manage_options' ), $user ) ) {
                    $message->is_admin_message = true;
                } else if ( apply_filters( 'helpgent_is_user_agent', in_array( 'helpgent_agent', $user->roles ) ) ) {
                    $message->is_agent_message = true;
                }
            }
        }

        if ( absint( $message->created_by ) === 0 ) {
            $message->is_agent_message = true;
        }

        if ( 'trash' === $message->status ) {
            $message->message        = __( 'The message was deleted', 'helpgent' );
            $message->attachment_id  = 0;
            $message->attachment_ids = [];
            $message->attachments    = [];

            return $message;
        }
        
        if ( ! empty( $message->attachment ) ) {
            $message->attachment = Repository::bind_file_url( $message->attachment );
        }

        $message->attachments = Repository::get_files_by_ids( array_values( (array) json_decode( (string) $message->attachment_ids, true ) ) );

        if ( ! empty( $message->parent ) ) {
            $this->prepare_message( $message->parent );
        }

        return apply_filters( 'helpgent_chat_message', $message );
    }

    public function create( ChatMessageDTO $message_dto ) {
        $form_response = $this->response_repository->get_by_id( $message_dto->get_response_id() );

        if ( ! $form_response ) {
            throw new Exception( esc_html__( 'Form response not found.', 'helpgent' ), 404 );
        }

        $data = [
            'response_id'    => $message_dto->get_response_id(),
            'is_read'        => $message_dto->get_is_read(),
            'created_by'     => $message_dto->get_created_by(),
            'replied_by'     => $message_dto->get_replied_by(),
            'attachment_ids' => wp_json_encode( $message_dto->get_attachment_ids() ),
            'created_at'     => helpgent_now()
        ];

        if ( ! empty( $message_dto->get_attachment_ids() ) ) {
            foreach ( $message_dto->get_attachment_ids() as $attachment_id_item ) {
                $this->response_repository->add_attachment( $message_dto->get_response_id(), $attachment_id_item );
            }
        }

        $message = $message_dto->get_message();
        
        if ( 'reply' === $message_dto->get_replied_by() ) {
            $data['message']   = $message;
            $data['parent_id'] = $message_dto->get_parent_id();

            return ChatMessage::query()->insert_get_id( $data );
        }

        $attachment_id = $message_dto->get_attachment_id();

        if ( in_array( $message_dto->get_replied_by(), [ 'video', 'screen_record' ] ) ) {
            $data['attachment_id'] = $attachment_id;
            $data['message']       = $message;

            if ( 0 !== $attachment_id ) {
                $this->response_repository->add_attachment( $message_dto->get_response_id(), $attachment_id );
            }

            return ChatMessage::query()->insert_get_id( $data );
        }

        if ( 'voice' === $message_dto->get_replied_by() ) {
            $data['attachment_id'] = $attachment_id;

            if ( 0 !== $attachment_id ) {
                $this->response_repository->add_attachment( $message_dto->get_response_id(), $attachment_id );
            }

        } else {
            $data['message'] = $message;
        }

        return ChatMessage::query()->insert_get_id( $data );
    }

    public function update( ChatMessageDTO $message_dto, int $edited_by ) {
        $message = ChatMessage::query()->where( 'id', $message_dto->get_id() )->where( 'response_id', $message_dto->get_response_id() )->first();

        if ( ! $message ) {
            throw new Exception( esc_html__( "Message not found.", "helpgent" ), 404 );
        }

        if ( $edited_by !== intval( $message->created_by ) ) {
            throw new Exception( esc_html__( "Sorry, you can't update this message.", "helpgent" ), 500 );
        }

        if ( 0 < $message->attachment_id ) {
            throw new Exception( esc_html__( "Sorry, you can't update the attachment.", "helpgent" ), 500 );
        }

        if ( ! empty( $message_dto->get_attachment_ids() ) ) {
            throw new Exception( esc_html__( "Sorry, you can't update the attachments.", "helpgent" ), 500 );
        }

        return ChatMessage::query()->where( 'id', $message_dto->get_id() )->where( 'response_id', $message_dto->get_response_id() )->update(
            [
                'message'    => $message_dto->get_message(),
                'updated_at' => helpgent_now(),
            ]
        );
    }

    public function update_bulk_read_status( array $ids, int $response_id, int $is_read ) {
        return ChatMessage::query()->where_in( 'id', $ids )->where( 'response_id', $response_id )->update(
            [
                'is_read'    => $is_read,
                'updated_at' => helpgent_now(),
            ]
        );
    }

    public function trash( int $id, int $response_id, int $delete_by ) {
        $message = ChatMessage::query()->where( 'id', $id )->where( 'response_id', $response_id )->first();

        if ( ! $message ) {
            throw new Exception( esc_html__( "Message not found.", "helpgent" ), 404 );
        }

        if ( $delete_by !== intval( $message->created_by ) ) {
            throw new Exception( esc_html__( "Sorry, you can't move this message to trash.", "helpgent" ), 500 );
        }

        return ChatMessage::query()
            ->where( 'id', $id )
            ->where( 'response_id', $response_id )
            ->update( [ 'status' => 'trash' ] );
    }
    
    public function restore( int $id, int $response_id, int $restore_by ) {
        $message = ChatMessage::query()->where( 'id', $id )->where( 'response_id', $response_id )->first();

        if ( ! $message ) {
            throw new Exception( esc_html__( "Message not found.", "helpgent" ), 404 );
        }

        if ( $restore_by !== intval( $message->created_by ) ) {
            throw new Exception( esc_html__( "Sorry, you can't restore this message.", "helpgent" ), 500 );
        }

        return ChatMessage::query()
            ->where( 'id', $id )
            ->where( 'response_id', $response_id )
            ->update( [ 'status' => 'publish' ] );
    }

    public function delete( int $id, int $response_id, int $delete_by ) {
        $message = ChatMessage::query()->where( 'id', $id )->where( 'response_id', $response_id )->first();

        if ( ! $message ) {
            throw new Exception( esc_html__( "Message not found.", "helpgent" ), 404 );
        }

        if ( $delete_by !== intval( $message->created_by ) ) {
            throw new Exception( esc_html__( "Sorry, you can't delete this message.", "helpgent" ), 500 );
        }

        $attachment_id = intval( $message->attachment_id );

        $delete = ChatMessage::query()->where( 'id', $id )->where( 'response_id', $response_id )->delete();

        if ( 0 !== $attachment_id ) {
            $this->response_repository->remove_attachment_permission( $response_id, $attachment_id );
        }

        $attachment_ids = json_decode( (string) $message->attachment_ids, true );

        if ( ! empty( $attachment_ids ) ) {
            foreach ( $attachment_ids as $attachment_id ) {
                $this->response_repository->remove_attachment_permission( $response_id, $attachment_id );
            }
        }

        return $delete;
    }

    public function get_admin_unread_message_count(): int {
        return ChatMessage::query( 'chat_message' )
            ->select( 'chat_message.id' )
            ->left_join( HelpGentUser::get_table_name() . ' as user', 'chat_message.created_by', 'user.id' )
            ->left_join( UserMeta::get_table_name() . ' as user_meta', 'user.registered_id', 'user_meta.user_id' )
            ->where( 'chat_message.is_read', '0' )
            ->where(
                function ( $builder ) {
                    $builder
                        ->where( 'chat_message.created_by', '0' )
                        ->or_where_is_null( 'user.registered_id' )
                        ->or_where( 
                            function ( $builder ) {
                                global $wpdb;

                                $builder
                                    ->where( "user_meta.meta_key", "{$wpdb->prefix}capabilities" )
                                    ->where(
                                        function( $builder ) {
                                            $builder
                                                ->where_not_like( "user_meta.meta_value", '%helpgent_agent%' )
                                                ->where_not_like( "user_meta.meta_value", '%administrator%' );
                                        } 
                                    );
                            } 
                        );
                } 
            )
            ->count();
    }

    public function get_response_unread_message_count( int $user_id, int $response_id ): int {
        return ChatMessage::query( 'chat_message' )
            ->select( 'chat_message.id' )
            ->where( 'chat_message.response_id', $response_id )
            ->where( 'chat_message.created_by', '!=', $user_id )
            ->where( 'is_read', '0' )
            ->count();
    }

    public function get_reviews( int $form_id, $type = null, int $page = 1, int $per_page = 10 ) {
        $query = ElementAnswers::query( 'answer' )
            ->select( 
                [
                    'answer.*',
                    'review.liked',
                    'review.comment',
                    'review.created_at as review_created_at',
                    'review.updated_at as review_updated_at',
                    "user.name as user_name", 
                    "user.email as user_email",
                ]
            )
            ->left_join( Review::get_table_name() . ' as review', 'answer.id', 'review.answer_id' )
            ->left_join( Response::get_table_name() . ' as response', 'answer.response_id', 'response.id' )
            ->left_join( HelpGentUser::get_table_name() . ' as user', 'response.created_by', 'user.id' )
            ->where( 'answer.form_id', $form_id )
            ->where( 'answer.element_type', LiveChat::get_key() );

        switch ( $type ) {
            case 'good':
                $query->where( 'review.liked', '1' );
                break;

            case 'bad':
                $query->where( 'review.liked', '0' );
                break;
            default:
                $query
                    ->where_not_null( 'review.liked' )
                    ->or_where_in( 'review.liked', ['0', '1'] );
                break;
        }

        return array_map( [ $this, 'prepare_review' ], $query->pagination( $page, $per_page, 1 ) );
    }

    public function prepare_review( stdClass $item ) {
        if ( $item->user_email ) {
            $item->user_avatar_url = get_avatar_url( $item->user_email );
        }

        return $item;
    }

    public function response_has_message( int $response_id ) {
        $message = ChatMessage::query()->where( 'response_id', $response_id )->first();
        return ! empty( $message ) ? true : false;
    }

    public function get_response_last_chat_agent_id( $response_id ): ?int {
        $query = ChatMessage::query( 'chat' )
            ->select( 'chat.created_by' )
            ->join(
                UserMeta::get_table_name() . ' as user_meta', function( JoinClause $join ) {
                    $join
                    ->on_column( 'chat.created_by', 'user_meta.user_id' )
                    ->on( 'user_meta.meta_key', '=', 'wp_capabilities' );
                } 
            )
            ->where( 'chat.response_id', $response_id )
            ->where(
                function ( $builder ) {
                    $builder
                    ->where_like( 'user_meta.meta_value', '%administrator%' )
                    ->or_where_like( 'user_meta.meta_value', '%helpgent_agent%' );
                } 
            )
            ->order_by( 'chat.id', 'desc' )
            ->first();


        return $query ? $query->created_by : null;
    }

    public function get_attachments( int $response_id, string $type, int $per_page, int $page ) {
        $attachment_table = Attachment::get_table_name();

        $query = ChatMessage::query( 'chat' )
            ->select( 
                [
                    'chat.*',
                    'user.name as user_name',
                    'user.email as user_email',
                ]
            )
            ->with(
                [
                    'attachment' => [ Repository::class, 'attachment_select_query' ],
                ]
            )
            ->left_join(
                Attachment::get_table_name() . ' as attachment', function( JoinClause $join ) use( $attachment_table ) {
                    $join->on_raw( "chat.attachment_id = attachment.id OR JSON_CONTAINS( chat.attachment_ids, JSON_ARRAY( attachment.id ) )" );
                }
            )
            ->left_join(
                HelpGentUser::get_table_name() . ' as user', function( $join ) {
                    $join->on_column( 'chat.created_by', 'user.id' );
                }
            )
            ->where( 'chat.response_id', $response_id );
        
        switch ( $type ) {
            case 'image':
                $query->where_like( "attachment.mime_type", "%image%" );
                break;
            case 'audio':
                $query->where_like( "attachment.mime_type", "%audio%" );
                break;
            case 'video':
                $query->where_like( "attachment.mime_type", "%video%" );
                break;
            case 'file':
                $query->where_raw( "( attachment.mime_type is not null and attachment.mime_type not like '%%image%%' and attachment.mime_type not like '%%video%%' and attachment.mime_type not like '%%audio%%' )" );
                break;
            default:
                $query->where_not_null( "attachment.mime_type" );
                break;
        }

        $query->group_by( 'chat.id' );

        return array_map( [ $this, 'prepare_message' ] , $query->pagination( $page, $per_page ) );
    }
}
