<?php

declare(strict_types=1);
defined('ABSPATH') || exit;

class DSProofwp_Reviews_Controller
{
    private const ROUTE_NAMESPACE = 'dsproofwp/v1';

    private DSProofwp_Review_Repository $review_repository;
    private DSProofwp_Review_Media_Repository $media_repository;
    private DSProofwp_Widget_Review_Repository $widget_review_repository;
    private DSProofwp_Recaptcha_Service $recaptcha_service;
    private DSProofwp_Form_Settings_Repository $form_settings_repository;

    public function __construct(
        DSProofwp_Review_Repository $review_repository,
        DSProofwp_Review_Media_Repository $media_repository,
        DSProofwp_Widget_Review_Repository $widget_review_repository,
        DSProofwp_Recaptcha_Service $recaptcha_service,
        DSProofwp_Form_Settings_Repository $form_settings_repository
    ) {
        $this->review_repository        = $review_repository;
        $this->media_repository         = $media_repository;
        $this->widget_review_repository = $widget_review_repository;
        $this->recaptcha_service        = $recaptcha_service;
        $this->form_settings_repository = $form_settings_repository;
    }

    public function register_routes(): void
    {
        register_rest_route(
            self::ROUTE_NAMESPACE,
            '/reviews',
            [
                [
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => [$this, 'get_reviews'],
                    'permission_callback' => '__return_true',
                ],
                [
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => [$this, 'create_review'],
                    'permission_callback' => '__return_true',
                ],
            ]
        );

        register_rest_route(
            self::ROUTE_NAMESPACE,
            '/reviews/sources',
            [
                [
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => [$this, 'get_sources'],
                    'permission_callback' => [$this, 'manage_permission'],
                ],
            ]
        );

        register_rest_route(
            self::ROUTE_NAMESPACE,
            '/reviews/(?P<id>\d+)',
            [
                [
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => [$this, 'get_review'],
                    'permission_callback' => [$this, 'manage_permission'],
                ],
                [
                    'methods'             => WP_REST_Server::EDITABLE,
                    'callback'            => [$this, 'update_review'],
                    'permission_callback' => [$this, 'manage_permission'],
                ],
                [
                    'methods'             => WP_REST_Server::DELETABLE,
                    'callback'            => [$this, 'delete_review'],
                    'permission_callback' => [$this, 'manage_permission'],
                ],
            ]
        );

        register_rest_route(
            self::ROUTE_NAMESPACE,
            '/reviews/(?P<id>\d+)/approve',
            [
                [
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => [$this, 'approve_review'],
                    'permission_callback' => [$this, 'manage_permission'],
                ],
            ]
        );

        register_rest_route(
            self::ROUTE_NAMESPACE,
            '/reviews/bulk-delete',
            [
                [
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => [$this, 'bulk_delete_reviews'],
                    'permission_callback' => [$this, 'manage_permission'],
                ],
            ]
        );

        register_rest_route(
            self::ROUTE_NAMESPACE,
            '/reviews/bulk-approve',
            [
                [
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => [$this, 'bulk_approve_reviews'],
                    'permission_callback' => [$this, 'manage_permission'],
                ],
            ]
        );
    }

    public function get_reviews(WP_REST_Request $request): WP_REST_Response
    {
        $include = $request->get_param('include');

        if (is_string($include)) {
            $include = array_filter(array_map('trim', explode(',', $include)));
        }

        $result = $this->review_repository->list([
            'include'    => is_array($include) ? $include : [],
            'product_id' => $request->get_param('product_id'),
            'rating'     => $request->get_param('rating'),
            'source'     => $request->get_param('source'),
            'sort'       => $request->get_param('sort'),
            'is_visual'  => $request->get_param('is_visual'),
            'status'     => $request->get_param('status'),
            'search'     => $request->get_param('search'),
            'page'       => $request->get_param('page'),
            'per_page'   => $request->get_param('per_page'),
        ]);

        $response = [
            'items'    => $this->attach_media($result['items']),
            'total'    => $result['total'],
            'page'     => $result['page'],
            'per_page' => $result['per_page'],
        ];

        $pending_filters = [
            'include'    => is_array($include) ? $include : [],
            'product_id' => $request->get_param('product_id'),
            'rating'     => $request->get_param('rating'),
            'source'     => $request->get_param('source'),
            'sort'       => $request->get_param('sort'),
            'is_visual'  => $request->get_param('is_visual'),
            'status'     => '0',
            'search'     => $request->get_param('search'),
            'page'       => 1,
            'per_page'   => 1,
        ];
        $pending_result = $this->review_repository->list($pending_filters);
        $response['pending_count'] = $pending_result['total'];

        $product_id = $request->get_param('product_id');
        if (! empty($product_id)) {
            $response['summary'] = $this->review_repository->get_summary((int) $product_id);
        }

        return rest_ensure_response($response);
    }

    public function get_review(WP_REST_Request $request)
    {
        $review = $this->review_repository->find((int) $request['id']);

        if (! $review) {
            return new WP_Error('dsproofwp_review_not_found', 'Review not found.', ['status' => 404]);
        }

        $items = $this->attach_media([$review]);

        return rest_ensure_response($items[0]);
    }

    public function create_review(WP_REST_Request $request)
    {
        // --- reCAPTCHA verification -------------------------------------------
        // Check if reCAPTCHA is enabled in form settings before verifying.
        $form_settings    = $this->form_settings_repository->get();
        $recaptcha_enabled = ! empty($form_settings['recaptcha_enabled']);
        $secret_key_set    = $this->recaptcha_service->has_secret_key();
        $is_admin          = current_user_can('moderate_comments') || current_user_can('manage_options');

        if (! $is_admin && $recaptcha_enabled && $secret_key_set) {
            $token = trim((string) ($request->get_param('recaptcha_token') ?? ''));

            if ($token === '') {
                return new WP_Error(
                    'dsproofwp_recaptcha_missing',
                    'reCAPTCHA token is required.',
                    ['status' => 400]
                );
            }

            if (! $this->recaptcha_service->verify_token($token)) {
                return new WP_Error(
                    'dsproofwp_recaptcha_invalid',
                    'reCAPTCHA verification failed. Please try again.',
                    ['status' => 400]
                );
            }
        }
        // ----------------------------------------------------------------------
        $payload = $this->sanitize_review_payload($request);

        $payload['is_visual'] = ! empty($payload['media']);

        // Handle verified status and author details for logged-in users
        if (is_user_logged_in()) {
            $user = wp_get_current_user();

            if (function_exists('wc_customer_bought_product')) {
                $payload['verified'] = wc_customer_bought_product($user->user_email, $user->ID, (int) $payload['product_id']);
            }

            $payload['user_id'] = $user->ID;

            if (empty($payload['author_name']) || $payload['author_name'] === 'Anonymous') {
                $payload['author_name'] = $user->display_name ?: $user->user_login;
            }

            if (empty($payload['author_email'])) {
                $payload['author_email'] = $user->user_email;
            }
        }

        $product_id   = (int) ($payload['product_id'] ?? 0);
        $user_id      = (int) ($payload['user_id'] ?? 0);
        $author_email = (string) ($payload['author_email'] ?? '');

        if ($this->review_repository->has_user_reviewed($product_id, $user_id, $author_email)) {
            return new WP_Error(
                'dsproofwp_review_already_submitted',
                __('You have already submitted a review for this product.', 'proofwp'),
                ['status' => 400]
            );
        }

        $review = $this->review_repository->create($payload);

        if (! $review) {
            return new WP_Error('dsproofwp_review_create_failed', 'Failed to create review.', ['status' => 500]);
        }

        return rest_ensure_response($this->store_or_load_media($review, $payload, false));
    }

    public function update_review(WP_REST_Request $request)
    {
        $review_id = (int) $request['id'];

        if (! $this->review_repository->find($review_id)) {
            return new WP_Error('dsproofwp_review_not_found', 'Review not found.', ['status' => 404]);
        }

        $payload = $this->sanitize_review_payload($request, true);

        if (array_key_exists('media', $payload)) {
            $payload['is_visual'] = ! empty($payload['media']);
        }

        $review = $this->review_repository->update($review_id, $payload);

        if (! $review) {
            return new WP_Error('dsproofwp_review_update_failed', 'Failed to update review.', ['status' => 500]);
        }

        return rest_ensure_response($this->store_or_load_media($review, $payload, true));
    }

    public function delete_review(WP_REST_Request $request): WP_REST_Response
    {
        $review_id = (int) $request['id'];

        $this->media_repository->delete_by_review_id($review_id);
        $this->widget_review_repository->delete_by_review_id($review_id);

        return rest_ensure_response([
            'deleted' => $this->review_repository->delete($review_id),
        ]);
    }

    public function bulk_delete_reviews(WP_REST_Request $request)
    {
        $ids = $request->get_param('ids');
        if (! is_array($ids)) {
            return new WP_Error('dsproofwp_invalid_ids', 'IDs must be an array.', ['status' => 400]);
        }

        $deleted_count = 0;
        foreach ($ids as $id) {
            $review_id = (int) $id;

            $this->media_repository->delete_by_review_id($review_id);
            $this->widget_review_repository->delete_by_review_id($review_id);

            if ($this->review_repository->delete($review_id)) {
                $deleted_count++;
            }
        }

        return rest_ensure_response([
            'deleted_count' => $deleted_count,
            'success'       => true,
        ]);
    }

    public function bulk_approve_reviews(WP_REST_Request $request)
    {
        $ids = $request->get_param('ids');
        if (! is_array($ids)) {
            return new WP_Error('dsproofwp_invalid_ids', 'IDs must be an array.', ['status' => 400]);
        }

        $approved_count = 0;
        foreach ($ids as $id) {
            $review_id = (int) $id;
            if ($this->review_repository->approve($review_id)) {
                $approved_count++;
            }
        }

        return rest_ensure_response([
            'approved_count' => $approved_count,
            'success'        => true,
        ]);
    }

    public function approve_review(WP_REST_Request $request): WP_REST_Response
    {
        $review_id = (int) $request['id'];

        return rest_ensure_response([
            'approved' => $this->review_repository->approve($review_id),
        ]);
    }

    public function get_sources(WP_REST_Request $request): WP_REST_Response
    {
        return rest_ensure_response($this->review_repository->get_used_sources());
    }

    public function manage_permission(): bool
    {
        return current_user_can('manage_options');
    }

    private function attach_media(array $reviews): array
    {
        $review_ids = array_map(
            static function (array $review): int {
                return (int) $review['id'];
            },
            $reviews
        );

        $media_map = $this->media_repository->find_by_review_ids($review_ids);

        foreach ($reviews as &$review) {
            $review['media'] = $media_map[$review['id']] ?? [];
        }

        return $reviews;
    }

    private function store_or_load_media(array $review, array $payload, bool $replace_existing): array
    {
        if (array_key_exists('media', $payload)) {
            if ($replace_existing) {
                $review['media'] = $this->media_repository->replace_for_review((int) $review['id'], $payload['media']);
            } else {
                $review['media'] = $this->media_repository->store_for_review((int) $review['id'], $payload['media']);
            }
        } else {
            $items           = $this->attach_media([$review]);
            $review['media'] = $items[0]['media'];
        }

        $review['is_visual'] = ! empty($review['media']);

        $updated_review = $this->review_repository->update((int) $review['id'], [
            'is_visual' => $review['is_visual'],
        ]);

        if ($updated_review) {
            $updated_review['media'] = $review['media'];

            return $updated_review;
        }

        return $review;
    }

    private function sanitize_review_payload(WP_REST_Request $request, bool $partial = false): array
    {
        $payload = [];

        $string_fields = [
            'author_name',
            'author_email',
            'content',
            'source',
            'status',
        ];

        foreach ($string_fields as $field) {
            $value = $request->get_param($field);

            if ($partial && $value === null) {
                continue;
            }

            $payload[$field] = $value;
        }

        $product_id = $request->get_param('product_id');
        if (! $partial || $product_id !== null) {
            $payload['product_id'] = absint($product_id);
        }

        $rating = $request->get_param('rating');
        if (! $partial || $rating !== null) {
            $payload['rating'] = $rating === null ? null : (int) $rating;
        }

        $verified = $request->get_param('verified');
        if (! $partial || $verified !== null) {
            $payload['verified'] = rest_sanitize_boolean($verified);
        }

        $media = $request->get_param('media');
        if ($media !== null) {
            $payload['media'] = is_array($media) ? $media : [];
        }

        return $payload;
    }
}
