<?php

namespace HelpGent\App\Http\Controllers;

defined( 'ABSPATH' ) || exit;

use Exception;
use HelpGent\App\DTO\UserDTO;
use HelpGent\App\Repositories\FormRepository;
use HelpGent\App\Repositories\ResponseRepository;
use WP_REST_Request;
use HelpGent\WpMVC\Routing\Response;
use HelpGent\WpMVC\RequestValidator\Validator;
use HelpGent\App\Http\Controllers\Controller;
use HelpGent\App\Repositories\AttachmentRepository;
use HelpGent\App\DTO\AttachmentDTO;
use HelpGent\App\DTO\ChunkFileDTO;
use HelpGent\App\DTO\AttachmentUploadSessionDTO;
use HelpGent\App\Repositories\AttachmentUploadSessionRepository;
use HelpGent\App\MediaDrivers\Local as LocalDrive;
use stdClass;

class AttachmentController extends Controller {
    public AttachmentRepository $attachment_repository;

    public ResponseRepository $response_repository;

    public AttachmentUploadSessionRepository $attachment_upload_session_repository;

    public string $attachment_driver;

    public function __construct( AttachmentRepository $attachment_repository, AttachmentUploadSessionRepository $attachment_upload_session_repository, ResponseRepository $response_repository ) {
        $this->attachment_repository                = $attachment_repository;
        $this->attachment_upload_session_repository = $attachment_upload_session_repository;
        $this->response_repository                  = $response_repository;
        $this->attachment_driver                    = helpgent_get_setting( 'active_media_driver', LocalDrive::get_key() );
    }

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

        $attachment = $this->attachment_repository->get_by_id( absint( $wp_rest_request->get_param( 'id' ) ) );

        if ( ! $attachment ) {
            throw new Exception( esc_html__( 'The attachment could not be found.', 'helpgent' ), 404 );
        }

        $attachment = helpgent_make_attachment_dto( $attachment );
        $user       = helpgent_get_current_user();

        if ( ! helpgent_can_user_access_attachment( $attachment, $user ) ) {
            throw new Exception( esc_html__( 'Access is forbidden.', 'helpgent' ), 403 );
        }

        return Response::send(
            [
                'id'        => $attachment->get_id(),
                'mime_type' => $attachment->get_mime_type(),
                'url'       => $this->attachment_repository->get_file_url( 
                    $attachment->get_title(), 
                    $attachment->get_file_id(), 
                    $attachment->get_storage()
                ),
            ]
        );
    }

    public function answer_store( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'form_id'    => 'required|numeric',
                'element_id' => 'required|string',
                'file'       => 'required|file',
            ]
        );

        $file = $wp_rest_request->get_file_params();
        $file = $file['file'];

        $attachment = $this->attachment_repository->upload_and_create( $file, $this->attachment_driver );

        return Response::send(
            [
                'id'        => $attachment->get_id(),
                'mime_type' => $attachment->get_mime_type(),
                'url'       => $this->attachment_repository->get_file_url( 
                    $attachment->get_title(), 
                    $attachment->get_file_id(), 
                    $attachment->get_storage()
                ),
                'token'     => $attachment->get_upload_token()
            ]
        );
    }

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

        $this->get_authorized_message_response(
            absint( $wp_rest_request->get_param( 'response_id' ) ),
            helpgent_get_current_user(),
            'upload_attachment'
        );

        $file = $wp_rest_request->get_file_params();
        $file = $file['file'];

        $attachment = $this->attachment_repository->upload_and_create( $file, $this->attachment_driver );

        return Response::send(
            [
                'id'        => $attachment->get_id(),
                'mime_type' => $attachment->get_mime_type(),
                'url'       => $this->attachment_repository->get_file_url( 
                    $attachment->get_title(), 
                    $attachment->get_file_id(), 
                    $attachment->get_storage()
                ),
            ]
        );
    }

    public function create_upload_id( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'file_size'   => 'required|numeric',
                'mime_type'   => 'required|string',
                'file_name'   => 'string|max:100',
                'response_id' => 'required|numeric'
            ]
        );

        $this->get_authorized_message_response(
            absint( $wp_rest_request->get_param( 'response_id' ) ),
            helpgent_get_current_user(),
            'create_upload_attachment'
        );

        $upload_session = $this->generate_upload_id(
            $wp_rest_request->get_param( 'file_size' ), 
            $wp_rest_request->get_param( 'mime_type' ),
            ( string ) $wp_rest_request->get_param( 'file_name' ) 
        );

        return Response::send(
            [ 
                'upload_id' => $upload_session->get_id(),
                'token'     => $upload_session->get_upload_token(),
            ] 
        );
    }

    public function create_answer_upload_id( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'file_size'  => 'required|numeric',
                'mime_type'  => 'required|string',
                'file_name'  => 'string|max:100',
                'form_id'    => 'required|numeric',
                'element_id' => 'required|string',
            ]
        );

        $upload_session = $this->generate_upload_id(
            $wp_rest_request->get_param( 'file_size' ), 
            $wp_rest_request->get_param( 'mime_type' ),
            ( string ) $wp_rest_request->get_param( 'file_name' )
        );

        return Response::send(
            [ 
                'upload_id' => $upload_session->get_id(),
                'token'     => $upload_session->get_upload_token(),
            ] 
        );
    }

    protected function get_authorized_message_response( int $response_id, ?UserDTO $user, string $context ): stdClass {
        $response = $this->response_repository->get_by_id( $response_id );

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

        $is_user_allowed = apply_filters(
            'helpgent_is_user_allowed_response_message',
            false,
            $response->id,
            $response,
            $user,
            $context
        );

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

        $can_store_message = apply_filters(
            'helpgent_can_store_response_message',
            $this->response_repository->can_user_store_message( $response, $user ),
            $response,
            $user,
            $context
        );

        if ( ! $can_store_message ) {
            throw new Exception( esc_html__( "Replies are not allowed for this response.", 'helpgent' ), 403 );
        }

        return $response;
    }

    protected function can_access_message_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 is_auth_user( UserDTO $user ): bool {
        return $user->is_administrator() || $user->is_agent();
    }

    /**
     * @throws Exception Sorry, this file type is not permitted.
     */
    public function generate_upload_id( string $file_size, string $mime_type, string $file_name = '' ): AttachmentUploadSessionDTO {
        $extension = helpgent_get_extension_from_mime_type( $mime_type );

        if ( empty( $extension ) ) {
            throw new Exception( esc_html__( 'Sorry, this file type is not permitted.', 'helpgent' ), 422 );
        }

        $file_name = ! empty( $file_name ) 
            ? $file_name
            : helpgent_generate_unique_key( 'file' ) . $extension;

        $file_name = helpgent_generate_unique_file_name( $file_name );

        $file = new AttachmentDTO;

        $file->set_title( $file_name );
        $file->set_mime_type( $mime_type );
        $file->set_file_size( $file_size );
        $file->set_storage( $this->attachment_driver );

        return $this->attachment_repository->create_upload_session( $file, $this->attachment_driver );
    }

    public function upload_chunk( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'upload_id'  => 'required|numeric',
                'token'      => 'required|string',
                'content'    => 'required|string|file',
                'range_from' => 'required|numeric',
                'range_to'   => 'required|numeric',
            ]
        );

        $upload_id    = ( int ) $wp_rest_request->get_param( 'upload_id' );
        $upload_token = $wp_rest_request->get_param( 'token' );

        $upload_session = $this->attachment_repository->get_attachment_upload_session( $upload_id, $upload_token );

        $content = ! empty( $wp_rest_request->get_param( 'content' ) ) ? $wp_rest_request->get_param( 'content' ) : '';
        $files   = $wp_rest_request->get_file_params();

        if ( isset( $files['content'] ) ) {
            $chunk     = $files['content'];
            $file_path = $chunk['tmp_name'];
            
            $content = file_get_contents( $file_path );
        }

        $chunk_file = ( new ChunkFileDTO() )
            ->set_id( $upload_session->get_id() )
            ->set_file_name( $upload_session->get_title() )
            ->set_content( $content )
            ->set_range_from( $wp_rest_request->get_param( 'range_from' ) )
            ->set_range_to( $wp_rest_request->get_param( 'range_to' ) )
            ->set_file_size( $upload_session->get_file_size() )
            ->set_mime_type( $upload_session->get_mime_type() );

        $upload_status = $this->attachment_repository->upload_chunk_and_create( $chunk_file, $upload_session );

        if ( $upload_status->get_is_done() && $upload_status->get_file() ) {
            return Response::send(
                [ 
                    'id'        => $upload_status->get_file()->get_id(),
                    'token'     => $upload_status->get_file()->get_upload_token(),
                    'mime_type' => $upload_status->get_file()->get_mime_type(),
                    'url'       => $this->attachment_repository->get_file_url(
                        $upload_status->get_file()->get_file_name(), 
                        $upload_status->get_file()->get_file_id(), 
                        $upload_status->get_file()->get_storage()
                    ),
                ]
            );
        }

        return Response::send(
            [
                'last_sent_bytes' => $upload_status->get_last_sent_bytes(),
                'message'         => esc_html__( 'The chunk data has been uploaded successfully!', 'helpgent' )
            ],
            201,
            [ 'last-sent-bytes' => $upload_status->get_last_sent_bytes() ]
        );
    }

    public function delete_answer( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $validator->validate(
            [
                'token'          => 'required|string',
                'form_id'        => 'required|numeric',
                'response_token' => 'string',
            ] 
        );

        /**
         * @var FormRepository $form_repository
         */
        $form_repository = helpgent_singleton( FormRepository::class );

        $form_id = intval( $wp_rest_request->get_param( 'form_id' ) );
        $form    = $form_repository->get_by_id( $form_id );

        if ( $form ) {
            $form_content = json_decode( $form->content, true );

            if ( '1' === helpgent_get_nested_value( "global.is_active_save_incomplete_data", $form_content ) ) {
                $validator->validate(
                    [
                        'response_token' => 'required|string',
                    ] 
                );

                $response = helpgent_get_response_by_token( $wp_rest_request->get_param( 'response_token' ), $form_id );

                if ( $response ) {
                    $this->response_repository->delete_attachment( $response->id, absint( $wp_rest_request->get_param( 'id' ) ) );
                }
            }
        }

        $attachment = $this->attachment_repository->get_by_upload_token( 
            absint( $wp_rest_request->get_param( 'id' ) ),
            $wp_rest_request->get_param( 'token' )
        );

        if ( ! $attachment ) {
            throw new Exception( esc_html__( 'The attachment could not be found.', 'helpgent' ), 404 );
        }

        $attachment = helpgent_make_attachment_dto( $attachment );

        $delete = $this->attachment_repository->delete( $attachment->get_id() );

        if ( 0 === $delete ) {
            throw new Exception( esc_html__( 'Oops, something went wrong. Please try again.', 'helpgent' ), 500 );
        }

        $this->attachment_repository->delete_file( $attachment->get_title(), $attachment->get_file_id(), $this->attachment_driver );

        return Response::send(
            [
                'message' => esc_html__( 'The attachment has been successfully deleted.', 'helpgent' )
            ]
        );
    }

    public function storage_summary( Validator $validator, WP_REST_Request $wp_rest_request ) {
        $this->attachment_repository->get_storage_summary();
        return Response::send( $this->attachment_repository->get_storage_summary() );
    }
}
