<?php

namespace WeDevs\DokanPro\REST;

use DSR_View;
use WeDevs\DokanPro\Modules\StoreReviews\Manager;
use WP_Error;
use WP_HTTP_Response;
use WP_REST_Response;
use WP_REST_Server;
use WeDevs\Dokan\Abstracts\DokanRESTController;

/**
 * Reviews API controller
 *
 * @package dokan
 * @since 2.8.0
 *
 * @author weDevs
 */
class ReviewsController extends DokanRESTController {

    /**
     * Endpoint namespace.
     *
     * @var string
     */
    protected $namespace = 'dokan/v1';

    /**
     * Route name
     *
     * @var string
     */
    protected $base = 'reviews';


    /**
     * Register all routes related with coupons
     *
     * @return void
     */
    public function register_routes() {
        register_rest_route(
            $this->namespace, '/' . $this->base, [
                [
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => [ $this, 'get_reviews' ],
                    'permission_callback' => [ $this, 'get_reviews_permission_check' ],
                    'args'                => $this->get_collection_params(),
                ],
            ]
        );

        register_rest_route(
            $this->namespace, '/' . $this->base . '/summary', [
                [
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => [ $this, 'get_reviews_summary' ],
                    'permission_callback' => [ $this, 'check_reviews_summary_permission' ],
                    'args'                => $this->get_collection_params(),
                ],
            ]
        );

        register_rest_route(
            $this->namespace, '/' . $this->base . '/(?P<id>[\d]+)', [
                'args' => [
                    'id' => [
                        'description' => __( 'Unique identifier for the object.', 'dokan' ),
                        'type'        => 'integer',
                    ],
                ],
                [
                    'methods'             => WP_REST_Server::EDITABLE,
                    'callback'            => [ $this, 'update_review_status' ],
                    'permission_callback' => [ $this, 'manage_reviews_permission_check' ],
                    'args'                => [
                        'status' => [
                            'description' => __( 'Review Status', 'dokan' ),
                            'required'    => true,
                            'type'        => 'string',
                        ],
                    ],
                ],
            ]
        );

        register_rest_route(
            $this->namespace, '/stores/(?P<id>[\d]+)/reviews', [
                'args' => [
                    'id' => [
                        'description'       => __( 'Unique identifier for the object.', 'dokan' ),
                        'type'              => 'integer',
                        'validate_callback' => [ $this, 'is_valid_store' ],
                    ],
                ],
                [
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => [ $this, 'create_item' ],
                    'permission_callback' => [ $this, 'create_item_permissions_check' ],
                    'args'                => [
                        'title'   => [
                            'required'    => true,
                            'type'        => 'string',
                            'description' => __( 'Review title.', 'dokan' ),
                        ],
                        'content' => [
                            'required'    => true,
                            'type'        => 'string',
                            'description' => __( 'Review content.', 'dokan' ),
                        ],
                        'rating'  => [
                            'required'          => false,
                            'type'              => 'integer',
                            'description'       => __( 'Review rating', 'dokan' ),
                            'validate_callback' => [ $this, 'is_valid_rating' ],
                            'default'           => 0,
                        ],
                    ],
                ],
            ]
        );

        // batch update reviews

        register_rest_route(
            $this->namespace, '/' . $this->base . '/batch-update', [
                [
                    'methods'             => WP_REST_Server::EDITABLE,
                    'callback'            => [ $this, 'batch_update_reviews' ],
                    'permission_callback' => [ $this, 'manage_reviews_permission_check' ],
                    'args'                => [
                        'reviews' => [
                            'required'    => true,
                            'type'        => 'array',
                            'description' => __( 'Array of reviews to update', 'dokan' ),
                            'items'       => [
                                'type'       => 'object',
                                'properties' => [
                                    'id'     => [
                                        'description' => __( 'Unique identifier for the object.', 'dokan' ),
                                        'type'        => 'integer',
                                    ],
                                    'status' => [
                                        'description' => __( 'Review Status', 'dokan' ),
                                        'required'    => true,
                                        'type'        => 'string',
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ]
        );

        // delete review
        register_rest_route(
            $this->namespace, '/' . $this->base . '/(?P<id>[\d]+)', [
                'args' => [
                    'id' => [
                        'description' => __( 'Unique identifier for the object.', 'dokan' ),
                        'type'        => 'integer',
                    ],
                ],
                [
                    'methods'             => WP_REST_Server::DELETABLE,
                    'callback'            => [ $this, 'delete_item' ],
                    'permission_callback' => [ $this, 'manage_reviews_permission_check' ],
                ],
            ]
        );
        // batch delete reviews
        register_rest_route(
            $this->namespace, '/' . $this->base . '/batch-delete', [
                [
                    'methods'             => WP_REST_Server::DELETABLE,
                    'callback'            => [ $this, 'batch_delete_reviews' ],
                    'permission_callback' => [ $this, 'manage_reviews_permission_check' ],
                    'args'                => [
                        'reviews' => [
                            'required'    => true,
                            'type'        => 'array',
                            'description' => __( 'Array of reviews to delete', 'dokan' ),
                            'items'       => [
                                'type'       => 'object',
                                'properties' => [
                                    'id' => [
                                        'description' => __( 'Unique identifier for the object.', 'dokan' ),
                                        'type'        => 'integer',
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ]
        );
    }

    /**
     * Validate store
     *
     * @param mixed $value
     * @param \WP_REST_Request $request
     * @param string $param
     *
     * @since 2.9.5
     *
     * @return bool|\WP_Error
     */
    public function is_valid_store( $value, $request, $param ) {
        $store = dokan()->vendor->get( $value );

        if ( absint( $store->id ) ) {
            return true;
        }

        return new WP_Error( 'rest_dokan_store_review_invalid_store', __( 'Invalid store id. Store not exists.', 'dokan' ) );
    }

    /**
     * Get reviews permissions
     *
     * @since 2.8.0
     *
     * @return bool
     */
    public function get_reviews_permission_check() {
        return current_user_can( 'dokan_view_review_menu' );
    }

    /**
     * Get reviews permissions
     *
     * @since 2.8.0
     *
     * @return bool
     */
    public function manage_reviews_permission_check(): bool {

        $has_permission = current_user_can( 'dokan_manage_reviews' );
        $seller_review_manage = dokan_get_option( 'seller_review_manage', 'dokan_selling', 'on' );

        if ( 'on' === $seller_review_manage && $has_permission ) {
            return true;
        }

        return false;
    }

    /**
     * Get reviews permissions
     *
     * @since 2.8.0
     *
     * @return bool
     */
    public function check_reviews_summary_permission() {
        return current_user_can( 'dokan_view_review_reports' );
    }

    /**
     * Get all reviews
     *
     * @param $request
     * @since 2.8.0
     * @return WP_Error|\WP_REST_Response /WP_REST_Response $response | \WP_Error
     */
    public function get_reviews( $request ) {
        $store_id     = dokan_get_current_user_id();
        $review_class = dokan_pro()->review;

        if ( empty( $store_id ) ) {
            return new WP_Error( 'no_store_found', __( 'No seller found', 'dokan' ), [ 'status' => 404 ] );
        }

        $limit  = $request['per_page'];
        $offset = ( $request['page'] - 1 ) * $request['per_page'];
        $search = isset( $request['search'] ) ? sanitize_text_field( $request['search'] ) : '';

        $status   = $this->get_status( $request );
        $comments = $review_class->comment_query( $store_id, 'product', $limit, $status, $offset, $search );

        $count = $this->get_total_count( $status, $search );

        $data = [];
        foreach ( $comments as $comment ) {
            $data[] = $this->prepare_item_for_response( $comment, $request );
        }

        $response = rest_ensure_response( $data );
        $response = $this->format_collection_response( $response, $request, $count );

        return $response;
    }

    /**
     * Create review permission callback
     *
     * @param \WP_REST_Request $request
     *
     * @since 2.9.5
     *
     * @return bool
     */
    public function create_item_permissions_check( $request ) {
        if ( ! dokan_pro()->module->is_active( 'store_reviews' ) ) {
            return false;
        }

        if ( current_user_can( 'dokan_manage_reviews' ) ) {
            return true;
        }

        $dsr_view = DSR_View::init();

        return $dsr_view->check_if_valid_customer( $request['id'], get_current_user_id() );
    }

    /**
     * Validate rating
     *
     * @param mixed $value
     * @param \WP_REST_Request $request
     * @param string $param
     *
     * @since 2.9.5
     *
     * @return bool|\WP_Error
     */
    public function is_valid_rating( $value, $request, $param ) {
        $rating = absint( $request['rating'] );

        if ( $rating >= 0 && $rating <= 5 ) {
            return true;
        }

        return false;
    }

    /**
     * Creates a store review
     *
     * @param \WP_REST_Request $request
     *
     * @since 2.9.5
     *
     * @return \WP_Error|\WP_REST_Response
     */
    public function create_item( $request ) {
        $manager = new Manager();
        $post_id = $manager->save_store_review(
            $request['id'],
            [
                'title'       => $request['title'],
                'content'     => $request['content'],
                'reviewer_id' => get_current_user_id(),
                'rating'      => $request['rating'],
            ]
        );

        if ( is_wp_error( $post_id ) ) {
            return $post_id;
        }

        $post = get_post( $post_id );

        $user          = get_user_by( 'id', $post->post_author );
        $user_gravatar = get_avatar_url( $user->user_email );

        $data = [
            'id'         => absint( $post->ID ),
            'author'     => [
                'id'     => $user->ID,
                'name'   => $user->user_login,
                'email'  => $user->user_email,
                'url'    => $user->user_url,
                'avatar' => $user_gravatar,
            ],
            'title'      => $post->post_title,
            'content'    => $post->post_content,
            'permalink'  => null,
            'product_id' => null,
            'approved'   => true,
            'date'       => mysql_to_rfc3339( $post->post_date ),
            'rating'     => absint( get_post_meta( $post->ID, 'rating', true ) ),
        ];

        return rest_ensure_response( $data );
    }

    /**
     * Manaage reviews
     * @since 2.8.0
     * @return WP_Error|WP_REST_Response
     */
    public function update_review_status( $request ) {
        $store_id     = dokan_get_current_user_id();
        $review_class = dokan_pro()->review;

        if ( empty( $store_id ) ) {
            return new WP_Error( 'no_store_found', __( 'No seller found', 'dokan' ), [ 'status' => 404 ] );
        }

        if ( empty( $request['id'] ) ) {
            return new WP_Error( 'no_reivew_found', __( 'No review id found', 'dokan' ), [ 'status' => 404 ] );
        }

        if ( empty( $request['status'] ) ) {
            return new WP_Error( 'no_reivew_status_found', __( 'No review status found for updating review', 'dokan' ), [ 'status' => 404 ] );
        }

        // check invalid status
        if ( ! in_array( $request['status'], [ 'hold', 'spam', 'trash','approve' ], true ) ) {
            return new WP_Error( 'invalid_status', __( 'Invalid status', 'dokan' ), [ 'status' => 400 ] );
        }

        $status = $this->get_status( $request );

        $comment_id = $request['id'];

        // check if the comment is valid
        $comment = get_comment( $comment_id );
        if ( ! $comment ) {
            return new WP_Error( 'no_reivew_found', __( 'No review found', 'dokan' ), [ 'status' => 404 ] );
        }

        // A vendor may only moderate reviews left on their own products; admins can moderate any.
        $product_author = (int) get_post_field( 'post_author', $comment->comment_post_ID );
        if ( $product_author !== $store_id && ! current_user_can( 'manage_woocommerce' ) ) {
            return new WP_Error( 'rest_forbidden', __( 'You do not have permission to manage this review.', 'dokan' ), [ 'status' => 403 ] );
        }

        wp_set_comment_status( $comment_id, $status );

        $comment = get_comment( $comment_id );
        $data    = $this->prepare_item_for_response( $comment, $request );

        return rest_ensure_response( $data );
    }

    /**
     * Get review status
     *
     * @since 2.8.0
     *
     * @return mixed
     */
    public function get_status( $request ) {
        $status = isset( $request['status'] ) ? $request['status'] : '';

        switch ( $status ) {
            case 'hold':
                return '0';
            case 'spam':
                return 'spam';
            case 'trash':
                return 'trash';
            default:
                return '1';
        }
    }

    /**
     * Get total count of comment
     *
     * @param string $status
     * @param string $search Optional. Filter count by author name/email match.
     *
     * @since 2.8.0
     * @since 5.0.5 Added optional `$search` parameter.
     *
     * @return int
     */
    public function get_total_count( $status, $search = '' ) {
        global $wpdb;
        $user_id = dokan_get_current_user_id();

        $search        = trim( (string) $search );
        $search_clause = '';
        $search_params = [];

        if ( '' !== $search ) {
            $like          = '%' . $wpdb->esc_like( $search ) . '%';
            $search_clause = " AND ( $wpdb->comments.comment_author LIKE %s OR $wpdb->comments.comment_author_email LIKE %s )";
            $search_params = [ $like, $like ];
        }

        $sql = "SELECT COUNT(*)
            FROM $wpdb->comments, $wpdb->posts
            WHERE $wpdb->posts.post_author = %d AND
            $wpdb->posts.post_status = 'publish' AND
            $wpdb->comments.comment_post_ID = $wpdb->posts.ID AND
            $wpdb->comments.comment_approved = %s AND
            $wpdb->posts.post_type = 'product'" . $search_clause;

        $params = array_merge( [ $user_id, $status ], $search_params );

        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        $total = $wpdb->get_var( $wpdb->prepare( $sql, $params ) );

        return $total;
    }

    /**
     * Get review summary
     * @since 2.8.0
     * @return WP_Error|WP_HTTP_Response|WP_REST_Response
     */
    public function get_reviews_summary( $request ) {
        $seller_id = dokan_get_current_user_id();

        $data = [
            'comment_counts' => dokan_count_comments( 'product', $seller_id ),
            'reviews_url'    => dokan_get_navigation_url( 'reviews' ),
        ];

        return rest_ensure_response( $data );
    }

    /**
     * Prepare a single product review output for response.
     *
     * @param  $review Product review object.
     * @param WP_REST_Request $request Request object.
     *
     * @return WP_REST_Response | WP_Error Response object on success, or WP_Error object on failure.
     */
    public function prepare_item_for_response( $review, $request ) {
        $data = [
            'id'           => (int) $review->comment_ID,
            'date_created' => wc_rest_prepare_date_response( $review->comment_date ),
            'review'       => $review->comment_content,
            'rating'       => (int) get_comment_meta( (int) $review->comment_ID, 'rating', true ),
            'name'         => $review->comment_author,
            'email'        => $review->comment_author_email,
            'verified'     => wc_review_is_from_verified_owner( (int) $review->comment_ID ),
            'link'        => get_comment_link( (int) $review->comment_ID ),
        ];

        $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
        $data    = $this->add_additional_fields_to_object( $data, $request );
        $data    = $this->filter_response_by_context( $data, $context );

        return apply_filters( 'woocommerce_rest_prepare_product_review', $data, $review, $request );
    }

    /**
     * Get the Product Review's schema, conforming to JSON Schema.
     *
     * @return array
     */
    public function get_item_schema() {
        $schema = [
            '$schema'    => 'http://json-schema.org/draft-04/schema#',
            'title'      => 'product_review',
            'type'       => 'object',
            'properties' => [
                'id'               => [
                    'description' => __( 'Unique identifier for the resource.', 'dokan' ),
                    'type'        => 'integer',
                    'context'     => [ 'view', 'edit' ],
                    'readonly'    => true,
                ],
                'review'           => [
                    'description' => __( 'The content of the review.', 'dokan' ),
                    'type'        => 'string',
                    'context'     => [ 'view', 'edit' ],
                ],
                'date_created'     => [
                    'description' => __( "The date the review was created, in the site's timezone.", 'dokan' ),
                    'type'        => 'date-time',
                    'context'     => [ 'view', 'edit' ],
                ],
                'date_created_gmt' => [
                    'description' => __( 'The date the review was created, as GMT.', 'dokan' ),
                    'type'        => 'date-time',
                    'context'     => [ 'view', 'edit' ],
                ],
                'rating'           => [
                    'description' => __( 'Review rating (0 to 5).', 'dokan' ),
                    'type'        => 'integer',
                    'context'     => [ 'view', 'edit' ],
                ],
                'name'             => [
                    'description' => __( 'Reviewer name.', 'dokan' ),
                    'type'        => 'string',
                    'context'     => [ 'view', 'edit' ],
                ],
                'email'            => [
                    'description' => __( 'Reviewer email.', 'dokan' ),
                    'type'        => 'string',
                    'context'     => [ 'view', 'edit' ],
                ],
                'verified'         => [
                    'description' => __( 'Shows if the reviewer bought the product or not.', 'dokan' ),
                    'type'        => 'boolean',
                    'context'     => [ 'view', 'edit' ],
                    'readonly'    => true,
                ],
            ],
        ];

        return $this->add_additional_fields_schema( $schema );
    }

    /**
     * Delete a review
     *
     * @since 2.9.5
     *
     * @param \WP_REST_Request $request
     *
     * @return WP_HTTP_Response | WP_Error
     */


    public function delete_item( $request ) {
        try {
            $store_id = dokan_get_current_user_id();

            if ( empty( $store_id ) ) {
                return new WP_Error( 'no_store_found', __( 'No seller found', 'dokan' ), [ 'status' => 404 ] );
            }

            if ( empty( $request['id'] ) ) {
                return new WP_Error( 'no_reivew_found', __( 'No review id found', 'dokan' ), [ 'status' => 404 ] );
            }

            $review_id = $request['id'];
            $result = $this->delete_review( $review_id, $store_id );

            if ( is_wp_error( $result ) ) {
                return $result;
            }

            return new WP_HTTP_Response(
                [
                    'message' => __( 'Review deleted successfully', 'dokan' ),
                    'status'  => 200,
                    'id'      => $review_id,
                ]
            );
        } catch ( Exception $e ) {
            return new WP_Error( 'error', $e->getMessage(), [ 'status' => 400 ] );
        }
    }

	//    Batch update reviews
    public function batch_update_reviews( $request ) {
        $store_id     = dokan_get_current_user_id();
        $review_class = dokan_pro()->review;

        if ( empty( $store_id ) ) {
            return new WP_Error( 'no_store_found', __( 'No seller found', 'dokan' ), [ 'status' => 404 ] );
        }

        if ( empty( $request['reviews'] ) ) {
            return new WP_Error( 'no_reivew_found', __( 'No review id found', 'dokan' ), [ 'status' => 404 ] );
        }

        $reviews = $request['reviews'];

        /**
         * Fires before batch updating reviews.
         *
         * @since 3.7.22
         *
         * @param array    $reviews   Array of reviews to be updated
         * @param int      $store_id  Current store/vendor ID
         * @param WP_REST_Request $request   The request object
         */
        do_action( 'dokan_before_batch_review_update', $reviews, $store_id, $request );

        $updated_reviews = [];
        foreach ( $reviews as $review ) {
            $comment_id = $review['id'];

            // check if the comment is valid
            $comment = get_comment( $comment_id );
            if ( ! $comment ) {
                continue;
            }

            // Vendors may only moderate reviews on products they own; skip anything belonging to another vendor.
            $product_author = (int) get_post_field( 'post_author', $comment->comment_post_ID );
            if ( $product_author !== $store_id && ! current_user_can( 'manage_woocommerce' ) ) {
                continue;
            }

            $status = $this->get_status( $review );

            wp_set_comment_status( $comment_id, $status );

            $comment = get_comment( $comment_id );
            $data    = $this->prepare_item_for_response( $comment, $request );

            $updated_reviews[] = $data;
        }

        /**
         * Fires after batch updating reviews.
         *
         * @since 3.7.22
         *
         * @param array    $updated_reviews   Array of updated review data
         * @param array    $reviews           Original array of reviews that were requested to update
         * @param int      $store_id          Current store/vendor ID
         * @param WP_REST_Request $request    The request object
         */
        do_action( 'dokan_after_batch_review_update', $updated_reviews, $reviews, $store_id, $request );

        return rest_ensure_response( $updated_reviews );
    }


    //    Batch delete reviews


    public function batch_delete_reviews( $request ) {
        $store_id = dokan_get_current_user_id();

        if ( empty( $store_id ) ) {
            return new WP_Error( 'no_store_found', __( 'No seller found', 'dokan' ), [ 'status' => 404 ] );
        }

        if ( empty( $request['reviews'] ) ) {
            return new WP_Error( 'no_reivew_found', __( 'No review id found', 'dokan' ), [ 'status' => 404 ] );
        }

        $reviews = $request['reviews'];

        /**
         * Fires before batch deleting reviews.
         *
         * @since 3.7.22
         *
         * @param array    $reviews   Array of reviews to be deleted
         * @param int      $store_id  Current store/vendor ID
         * @param WP_REST_Request $request   The request object
         */
        do_action( 'dokan_before_batch_review_delete', $reviews, $store_id, $request );

        $deleted_reviews = [];
        $failed_reviews = [];

        foreach ( $reviews as $review ) {
            $comment_id = $review['id'];
            $result = $this->delete_review( $comment_id, $store_id );

            if ( is_wp_error( $result ) ) {
                $failed_reviews[] = [
                    'id' => $comment_id,
                    'message' => $result->get_error_message(),
                ];
                continue;
            }

            $deleted_reviews[] = $comment_id;
        }

        /**
         * Fires after batch deleting reviews.
         *
         * @since 3.7.22
         *
         * @param array    $deleted_reviews  Array of successfully deleted review IDs
         * @param array    $failed_reviews   Array of reviews that failed to delete with error messages
         * @param array    $reviews          Original array of reviews that were requested to delete
         * @param int      $store_id         Current store/vendor ID
         * @param WP_REST_Request $request   The request object
         */
        do_action( 'dokan_after_batch_review_delete', $deleted_reviews, $failed_reviews, $reviews, $store_id, $request );

        return rest_ensure_response( $deleted_reviews );
    }

    /**
     * Helper method to delete a review
     *
     * @since 3.7.22
     *
     * @param int $review_id The review ID to delete
     * @param int $store_id The store/vendor ID
     *
     * @return array|WP_Error Array with status and message on success, WP_Error on failure
     */
    protected function delete_review( $review_id, $store_id ) {
        // check if the comment is valid
        $comment = get_comment( $review_id );
        if ( ! $comment ) {
            return new WP_Error( 'no_review_found', __( 'No review found', 'dokan' ), [ 'status' => 404 ] );
        }

        // A vendor may only delete reviews left on their own products; admins can moderate any review.
        $product_author = (int) get_post_field( 'post_author', $comment->comment_post_ID );
        if ( $product_author !== (int) $store_id && ! current_user_can( 'manage_woocommerce' ) ) {
            return new WP_Error( 'rest_forbidden', __( 'Sorry, you are not allowed to delete this review.', 'dokan' ), [ 'status' => 403 ] );
        }

        // check if the comment status is not trash then can't delete
        if ( 'trash' !== $comment->comment_approved ) {
            return new WP_Error( 'cannot_delete_review', __( 'Can not delete review', 'dokan' ), [ 'status' => 404 ] );
        }

        /**
         * Fires before a review is deleted.
         *
         * @since 3.7.22
         *
         * @param int        $review_id The review ID being deleted
         * @param WP_Comment $comment   The comment/review object
         * @param int        $store_id  Current store/vendor ID
         */
        do_action( 'dokan_before_review_delete', $review_id, $comment, $store_id );

        $deleted = wp_delete_comment( $review_id );

        if ( ! $deleted ) {
            return new WP_Error( 'error', __( 'Error deleting review', 'dokan' ), [ 'status' => 400 ] );
        }

        /**
         * Fires after a review is deleted.
         *
         * @since 3.7.22
         *
         * @param int  $review_id The review ID that was deleted
         * @param bool $deleted   Whether the deletion was successful
         * @param int  $store_id  Current store/vendor ID
         */
        do_action( 'dokan_after_review_delete', $review_id, $deleted, $store_id );

        return [
            'deleted' => true,
            'id'      => $review_id,
        ];
    }
}
