<?php
/**
 * WooCommerce product reviews — list and moderation for ZERTI inventory.
 *
 * Uses the same comment_type set as WooCommerce (review, comment, legacy empty) on products.
 */

if (!defined('ABSPATH')) {
    exit;
}

function zerti_product_reviews_available(): bool
{
    return class_exists('WooCommerce') && post_type_exists('product');
}

function zerti_product_reviews_capability(): string
{
    return 'moderate_comments';
}

function zerti_user_can_manage_product_reviews(): bool
{
    if (current_user_can(zerti_product_reviews_capability())) {
        return true;
    }

    return current_user_can('edit_products') || current_user_can('manage_woocommerce');
}

/**
 * Comment types WooCommerce treats as product reviews (legacy / admin paths may use '' or 'comment').
 *
 * @see WC_Comments::get_review_counts_for_product_ids()
 */
function zerti_product_review_wc_comment_types(): array
{
    return ['review', 'comment', ''];
}

/**
 * Whether a comment is a listable top-level product review for ZERTI (matches WooCommerce counting rules).
 */
function zerti_product_review_is_wc_product_review(WP_Comment $comment): bool
{
    if ((int) $comment->comment_post_ID < 1) {
        return false;
    }

    if (get_post_type((int) $comment->comment_post_ID) !== 'product') {
        return false;
    }

    return in_array((string) $comment->comment_type, zerti_product_review_wc_comment_types(), true)
        && (int) $comment->comment_parent === 0;
}

/**
 * @return array<string, string>
 */
function zerti_product_review_status_choices(): array
{
    return [
        'approve' => __('Approved', 'zerti'),
        'hold' => __('Pending review', 'zerti'),
        'spam' => __('Spam', 'zerti'),
        'trash' => __('Trash', 'zerti'),
    ];
}

/**
 * Normalize {@see wp_get_comment_status()} to keys used in UI and {@see WP_Comment_Query} (`approve`, `hold`, …).
 */
function zerti_product_review_status_for_ui(WP_Comment $comment): string
{
    $raw = wp_get_comment_status($comment);
    if ($raw === false || $raw === '') {
        return 'hold';
    }

    $raw = (string) $raw;
    static $map = [
        'approved' => 'approve',
        'unapproved' => 'hold',
    ];

    return $map[$raw] ?? $raw;
}

/**
 * @return array<string, mixed>|null
 */
function zerti_product_review_to_item(WP_Comment $comment): ?array
{
    if (!zerti_product_review_is_wc_product_review($comment)) {
        return null;
    }

    $product_id = (int) $comment->comment_post_ID;
    $product = $product_id > 0 ? wc_get_product($product_id) : null;
    $product_name = '';
    if ($product) {
        $product_name = $product->get_name();
    } elseif ($product_id > 0) {
        $product_name = get_the_title($product_id);
    }
    if ($product_name === '') {
        $product_name = __('Deleted product', 'zerti');
    }

    $rating = (int) get_comment_meta((int) $comment->comment_ID, 'rating', true);
    if ($rating < 0) {
        $rating = 0;
    }
    if ($rating > 5) {
        $rating = 5;
    }

    $status = zerti_product_review_status_for_ui($comment);

    $ts = strtotime((string) $comment->comment_date_gmt . ' UTC');
    if ($ts === false) {
        $ts = 0;
    }
    if ($ts <= 0) {
        $local_ts = strtotime((string) $comment->comment_date);
        if ($local_ts !== false) {
            $ts = $local_ts;
        }
    }

    return [
        'id' => (int) $comment->comment_ID,
        'product_id' => $product_id,
        'product_name' => $product_name,
        'product_url' => $product_id > 0
            ? zerti_admin_route_url([
                'tab' => 'inventory',
                'inv' => 'warehouse',
                'product' => $product_id,
            ])
            : '',
        'author' => trim((string) $comment->comment_author) !== ''
            ? (string) $comment->comment_author
            : __('Guest', 'zerti'),
        'author_user_id' => (int) $comment->user_id,
        'customer_url' => (int) $comment->user_id > 0
            ? zerti_admin_route_url([
                'tab' => 'customers',
                'customer' => (int) $comment->user_id,
            ])
            : '',
        'author_email' => (string) $comment->comment_author_email,
        'content' => (string) $comment->comment_content,
        'rating' => $rating,
        'status' => $status,
        'status_label' => zerti_product_review_status_label($status),
        'date' => $ts > 0 && function_exists('zerti_format_date')
            ? zerti_format_date(new DateTime('@' . $ts))
            : '—',
        'submitted_at' => $ts > 0 && function_exists('zerti_format_datetime')
            ? zerti_format_datetime($ts)
            : '—',
        'date_sort' => $ts,
        'verified' => (bool) get_comment_meta((int) $comment->comment_ID, 'verified', true),
        'reply_id' => 0,
        'reply' => '',
    ];
}

/**
 * Whether $reply is a direct reply comment on a product review (child of $review_id on $post_id).
 */
function zerti_product_review_reply_belongs_to_review(WP_Comment $reply, int $review_id, int $post_id): bool
{
    if ($review_id < 1 || $post_id < 1) {
        return false;
    }

    return (int) $reply->comment_parent === $review_id
        && (int) $reply->comment_post_ID === $post_id
        && (int) $reply->comment_ID !== $review_id;
}

/**
 * Oldest direct child comment for a review (WooCommerce / WP store thread replies as children).
 *
 * @return array{id:int,content:string}|null
 */
function zerti_product_review_get_first_reply_row(int $review_id): ?array
{
    if ($review_id < 1) {
        return null;
    }

    $rows = get_comments([
        'post_type' => 'product',
        'parent' => $review_id,
        'status' => 'all',
        'orderby' => 'comment_date_gmt',
        'order' => 'ASC',
        'number' => 1,
    ]);

    if (!is_array($rows) || $rows === [] || !$rows[0] instanceof WP_Comment) {
        return null;
    }

    $c = $rows[0];

    return [
        'id' => (int) $c->comment_ID,
        'content' => (string) $c->comment_content,
    ];
}

/**
 * First reply per review id (oldest child) for batched list enrichment.
 *
 * @param list<int> $review_ids
 * @return array<int, array{id:int, content:string}>
 */
function zerti_product_review_first_replies_for_reviews(array $review_ids): array
{
    $review_ids = array_values(array_unique(array_filter(array_map('intval', $review_ids))));
    if ($review_ids === []) {
        return [];
    }

    $children = get_comments([
        'post_type' => 'product',
        'parent__in' => $review_ids,
        'status' => 'all',
        'orderby' => 'comment_date_gmt',
        'order' => 'ASC',
        'number' => 2000,
    ]);

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

    $map = [];
    foreach ($children as $c) {
        if (!$c instanceof WP_Comment) {
            continue;
        }
        $pid = (int) $c->comment_parent;
        if (!in_array($pid, $review_ids, true)) {
            continue;
        }
        if (!isset($map[$pid])) {
            $map[$pid] = [
                'id' => (int) $c->comment_ID,
                'content' => (string) $c->comment_content,
            ];
        }
    }

    return $map;
}

/**
 * @param array<string, mixed> $item Row from {@see zerti_product_review_to_item()}.
 * @return array<string, mixed>
 */
function zerti_product_review_item_with_reply(array $item): array
{
    $rid = (int) ($item['id'] ?? 0);
    if ($rid < 1) {
        $item['reply_id'] = 0;
        $item['reply'] = '';

        return $item;
    }

    $row = zerti_product_review_get_first_reply_row($rid);
    if ($row !== null) {
        $item['reply_id'] = $row['id'];
        $item['reply'] = $row['content'];
    } else {
        $item['reply_id'] = 0;
        $item['reply'] = '';
    }

    return $item;
}

/**
 * Create, update, or remove the first thread reply (child comment) for a product review.
 *
 * @return array{ok:bool,message?:string,code?:string}
 */
function zerti_product_review_sync_reply(int $review_id, int $post_id, string $reply_raw, int $client_reply_id): array
{
    if ($review_id < 1 || $post_id < 1) {
        return ['ok' => false, 'message' => __('Not found', 'zerti'), 'code' => 'not_found'];
    }

    $reply_text = trim(wp_kses_post($reply_raw));
    $existing_row = zerti_product_review_get_first_reply_row($review_id);
    $existing_id = $existing_row !== null ? (int) $existing_row['id'] : 0;

    if ($reply_text === '') {
        if ($existing_id > 0) {
            $deleted = wp_delete_comment($existing_id, true);
            if (!$deleted) {
                return ['ok' => false, 'message' => __('Reply save failed', 'zerti'), 'code' => 'reply_save_failed'];
            }
        }

        return ['ok' => true];
    }

    $target_id = 0;
    if ($client_reply_id > 0) {
        $c = get_comment($client_reply_id);
        if ($c instanceof WP_Comment && zerti_product_review_reply_belongs_to_review($c, $review_id, $post_id)) {
            $target_id = $client_reply_id;
        }
    }
    if ($target_id < 1 && $existing_id > 0) {
        $target_id = $existing_id;
    }

    if ($target_id > 0) {
        $updated = wp_update_comment([
            'comment_ID' => $target_id,
            'comment_content' => $reply_text,
        ]);
        if ($updated === false || is_wp_error($updated)) {
            $msg = is_wp_error($updated) ? $updated->get_error_message() : __('Reply save failed', 'zerti');

            return ['ok' => false, 'message' => $msg, 'code' => 'reply_save_failed'];
        }

        return ['ok' => true];
    }

    $user = wp_get_current_user();
    $new_id = wp_insert_comment([
        'comment_post_ID' => $post_id,
        'comment_parent' => $review_id,
        'comment_content' => $reply_text,
        'comment_author' => $user->display_name !== '' ? $user->display_name : $user->user_login,
        'comment_author_email' => $user->user_email !== '' ? $user->user_email : '',
        'comment_author_url' => '',
        'user_id' => (int) $user->ID,
        'comment_approved' => '1',
        'comment_type' => 'comment',
    ]);

    if (!$new_id || $new_id < 1) {
        return ['ok' => false, 'message' => __('Reply save failed', 'zerti'), 'code' => 'reply_save_failed'];
    }

    return ['ok' => true];
}

function zerti_product_review_status_label(string $status): string
{
    $choices = zerti_product_review_status_choices();
    static $wp_aliases = [
        'approved' => 'approve',
        'unapproved' => 'hold',
    ];
    if (isset($wp_aliases[$status])) {
        $status = $wp_aliases[$status];
    }

    return $choices[$status] ?? $status;
}

/**
 * @return list<string>
 */
function zerti_product_feedback_kinds(): array
{
    return ['review', 'comment'];
}

function zerti_product_feedback_kind_is_valid(string $kind): bool
{
    return in_array($kind, zerti_product_feedback_kinds(), true);
}

/**
 * WooCommerce comment types for a feedback kind (reviews vs product comments).
 *
 * @return list<string>
 */
function zerti_product_feedback_comment_types(string $kind): array
{
    if ($kind === 'comment') {
        return ['comment'];
    }

    // Include legacy empty type; zerti_product_feedback_comment_matches_kind() keeps star ratings only.
    return ['review', ''];
}

/**
 * Whether a product comment belongs in the reviews or comments tab.
 */
function zerti_product_feedback_comment_matches_kind(WP_Comment $comment, string $kind): bool
{
    if (!zerti_product_feedback_kind_is_valid($kind)) {
        return false;
    }

    $type = (string) $comment->comment_type;

    if ($kind === 'review') {
        if ($type === 'review') {
            return true;
        }

        // Legacy WooCommerce rows: empty type with a star rating are reviews.
        if ($type === '' && (int) get_comment_meta((int) $comment->comment_ID, 'rating', true) > 0) {
            return true;
        }

        return false;
    }

    return $type === 'comment';
}

function zerti_product_feedback_kind_from_array(array $source, string $default = 'review'): string
{
    if (!isset($source['kind'])) {
        return zerti_product_feedback_kind_is_valid($default) ? $default : 'review';
    }

    $kind = sanitize_key(wp_unslash((string) $source['kind']));

    return zerti_product_feedback_kind_is_valid($kind) ? $kind : 'review';
}

/**
 * Pending product feedback count for sidebar badges (WooCommerce status: hold).
 */
function zerti_product_pending_feedback_count(string $kind, bool $refresh = false): int
{
    static $request_cache = [];

    if ($refresh) {
        unset($request_cache[$kind]);
    }

    if (array_key_exists($kind, $request_cache)) {
        return $request_cache[$kind];
    }

    if (
        !zerti_product_feedback_kind_is_valid($kind)
        || !zerti_product_reviews_available()
        || !zerti_user_can_manage_product_reviews()
    ) {
        $request_cache[$kind] = 0;

        return 0;
    }

    $comments = get_comments([
        'type__in' => zerti_product_feedback_comment_types($kind),
        'post_type' => 'product',
        'parent' => 0,
        'status' => 'hold',
        'number' => 0,
        'count' => false,
    ]);

    $count = 0;
    if (is_array($comments)) {
        foreach ($comments as $comment) {
            if ($comment instanceof WP_Comment && zerti_product_feedback_comment_matches_kind($comment, $kind)) {
                ++$count;
            }
        }
    }

    $request_cache[$kind] = $count;

    return $request_cache[$kind];
}

/**
 * Pending product reviews count for sidebar badges (WooCommerce status: hold).
 */
function zerti_product_pending_reviews_count(bool $refresh = false): int
{
    return zerti_product_pending_feedback_count('review', $refresh);
}

/**
 * Pending product comments count for sidebar badges (WooCommerce status: hold).
 */
function zerti_product_pending_comments_count(bool $refresh = false): int
{
    return zerti_product_pending_feedback_count('comment', $refresh);
}

/**
 * Combined pending reviews + comments for Customer affairs parent badge.
 */
function zerti_product_pending_customer_relations_count(bool $refresh = false): int
{
    return zerti_product_pending_reviews_count($refresh) + zerti_product_pending_comments_count($refresh);
}

function zerti_product_pending_feedback_badge_label(int $count): string
{
    if ($count < 1) {
        return '';
    }

    $display = $count > 99 ? '99+' : (string) $count;

    return zerti_to_persian_digits($display);
}

/**
 * Badge label for nav (caps at 99+).
 */
function zerti_product_pending_reviews_badge_label(): string
{
    return zerti_product_pending_feedback_badge_label(zerti_product_pending_reviews_count());
}

/**
 * Badge label for pending product comments.
 */
function zerti_product_pending_comments_badge_label(): string
{
    return zerti_product_pending_feedback_badge_label(zerti_product_pending_comments_count());
}

/**
 * Badge label for combined Customer affairs pending feedback.
 */
function zerti_product_pending_customer_relations_badge_label(): string
{
    return zerti_product_pending_feedback_badge_label(zerti_product_pending_customer_relations_count());
}

/**
 * @return list<array<string, mixed>>
 */
function zerti_product_reviews_list(string $search = '', string $status_filter = '', string $kind = 'review'): array
{
    if (!zerti_product_reviews_available()) {
        return [];
    }

    if (!zerti_product_feedback_kind_is_valid($kind)) {
        $kind = 'review';
    }

    $args = [
        'type__in' => zerti_product_feedback_comment_types($kind),
        'post_type' => 'product',
        'parent' => 0,
        'status' => 'all',
        'number' => 500,
        'orderby' => 'comment_date_gmt',
        'order' => 'DESC',
    ];

    if ($status_filter !== '' && array_key_exists($status_filter, zerti_product_review_status_choices())) {
        $args['status'] = $status_filter;
    }

    if ($search !== '') {
        $args['search'] = $search;
    }

    $comments = get_comments($args);
    if (!is_array($comments)) {
        return [];
    }

    $items = [];
    foreach ($comments as $comment) {
        if (!$comment instanceof WP_Comment) {
            continue;
        }
        if (!zerti_product_feedback_comment_matches_kind($comment, $kind)) {
            continue;
        }
        $row = zerti_product_review_to_item($comment);
        if ($row !== null) {
            $items[] = $row;
        }
    }

    if ($items !== []) {
        $reply_map = zerti_product_review_first_replies_for_reviews(array_column($items, 'id'));
        foreach ($items as $i => $row) {
            $rid = (int) ($row['id'] ?? 0);
            if ($rid > 0 && isset($reply_map[$rid])) {
                $items[$i]['reply_id'] = $reply_map[$rid]['id'];
                $items[$i]['reply'] = $reply_map[$rid]['content'];
            }
        }
    }

    return $items;
}

/**
 * @param array<string, mixed> $source
 * @return array{id:int,content:string,rating:int,status:string,reply:string,reply_id:int}
 */
function zerti_product_review_payload_from_array(array $source): array
{
    $id = isset($source['id']) ? absint(wp_unslash((string) $source['id'])) : 0;
    $content = isset($source['content']) ? wp_kses_post(wp_unslash((string) $source['content'])) : '';
    $rating = isset($source['rating']) ? absint(wp_unslash((string) $source['rating'])) : 0;
    $status = isset($source['status']) ? sanitize_key(wp_unslash((string) $source['status'])) : '';
    $reply = isset($source['reply']) ? wp_kses_post(wp_unslash((string) $source['reply'])) : '';
    $reply_id = isset($source['reply_id']) ? absint(wp_unslash((string) $source['reply_id'])) : 0;

    return [
        'id' => $id,
        'content' => $content,
        'rating' => $rating,
        'status' => $status,
        'reply' => $reply,
        'reply_id' => $reply_id,
    ];
}

/**
 * @return array{id:int,content:string,rating:int,status:string,reply:string,reply_id:int}
 */
/**
 * @param array{id:int,content:string,rating:int,status:string,reply?:string,reply_id?:int} $payload
 * @return array{ok:bool,item?:array<string,mixed>,message?:string,code?:string}
 */
function zerti_product_review_save(array $payload): array
{
    if (!zerti_product_reviews_available()) {
        return ['ok' => false, 'message' => __('Reviews unavailable', 'zerti'), 'code' => 'reviews_unavailable'];
    }

    $id = (int) ($payload['id'] ?? 0);
    if ($id < 1) {
        return ['ok' => false, 'message' => __('Not found', 'zerti'), 'code' => 'not_found'];
    }

    $comment = get_comment($id);
    if (!$comment instanceof WP_Comment || !zerti_product_review_is_wc_product_review($comment)) {
        return ['ok' => false, 'message' => __('Not found', 'zerti'), 'code' => 'not_found'];
    }

    $post_id = (int) $comment->comment_post_ID;

    $rating = max(0, min(5, (int) ($payload['rating'] ?? 0)));
    $content = trim((string) ($payload['content'] ?? ''));
    $status = sanitize_key((string) ($payload['status'] ?? ''));
    $choices = zerti_product_review_status_choices();

    $update = ['comment_ID' => $id];
    if ($content !== '') {
        $update['comment_content'] = $content;
    }

    $updated = wp_update_comment($update);
    if ($updated === false || is_wp_error($updated)) {
        $msg = is_wp_error($updated) ? $updated->get_error_message() : __('Save failed', 'zerti');

        return ['ok' => false, 'message' => $msg, 'code' => 'save_failed'];
    }

    update_comment_meta($id, 'rating', $rating);

    if ($status !== '' && array_key_exists($status, $choices)) {
        wp_set_comment_status($id, $status);
    }

    $fresh = get_comment($id);
    if (!$fresh instanceof WP_Comment) {
        return ['ok' => false, 'message' => __('Save failed', 'zerti'), 'code' => 'save_failed'];
    }

    $item = zerti_product_review_to_item($fresh);
    if ($item === null) {
        return ['ok' => false, 'message' => __('Save failed', 'zerti'), 'code' => 'save_failed'];
    }

    $reply_sync = zerti_product_review_sync_reply(
        $id,
        $post_id,
        (string) ($payload['reply'] ?? ''),
        (int) ($payload['reply_id'] ?? 0)
    );
    if (empty($reply_sync['ok'])) {
        return $reply_sync;
    }

    $item = zerti_product_review_item_with_reply($item);

    if (function_exists('zerti_invalidate_sidebar_badge_counts')) {
        zerti_invalidate_sidebar_badge_counts();
    }

    return ['ok' => true, 'item' => $item];
}

/**
 * @return array{ok:bool,message?:string,code?:string}
 */
function zerti_product_review_delete(int $comment_id, bool $force = false): array
{
    if ($comment_id < 1 || !zerti_product_reviews_available()) {
        return ['ok' => false, 'message' => __('Not found', 'zerti'), 'code' => 'not_found'];
    }

    $comment = get_comment($comment_id);
    if (!$comment instanceof WP_Comment || !zerti_product_review_is_wc_product_review($comment)) {
        return ['ok' => false, 'message' => __('Not found', 'zerti'), 'code' => 'not_found'];
    }

    $post_id = (int) $comment->comment_post_ID;
    $children = get_comments([
        'post_id' => $post_id,
        'parent' => $comment_id,
        'status' => 'all',
        'number' => 200,
    ]);
    if (is_array($children)) {
        foreach ($children as $ch) {
            if ($ch instanceof WP_Comment) {
                wp_delete_comment((int) $ch->comment_ID, $force);
            }
        }
    }

    $deleted = wp_delete_comment($comment_id, $force);
    if (!$deleted) {
        return ['ok' => false, 'message' => __('Delete failed', 'zerti'), 'code' => 'delete_failed'];
    }

    if (function_exists('zerti_invalidate_sidebar_badge_counts')) {
        zerti_invalidate_sidebar_badge_counts();
    }

    return ['ok' => true];
}

function zerti_product_review_error_message(string $code, string $fallback = ''): string
{
    $map = [
        'not_found' => __('Review not found.', 'zerti'),
        'reviews_unavailable' => __('Product reviews are not available.', 'zerti'),
        'delete_failed' => __('Could not delete review.', 'zerti'),
        'save_failed' => __('Could not save review.', 'zerti'),
        'reply_save_failed' => __('Could not save store reply.', 'zerti'),
    ];

    if (isset($map[$code])) {
        return $map[$code];
    }

    return $fallback !== '' ? $fallback : __('An error occurred.', 'zerti');
}
