<?php

namespace FleekDash\Controllers;

use FleekDash\Libs\API\ResponseService;
// Use fully-qualified names in code to avoid namespace issues

/**
 * Class CommentController
 *
 * Handles comment-related API endpoints.
 *
 * @package FleekDash\Controllers
 */
class CommentController
{
    private const MSG_COMMENT_NOT_FOUND = 'Comment not found';

    /**
     * Get a paginated list of comments
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function index($request)
    {
        try {
            $page = (int) ($request->get_param('page') ?? 1);
            $per_page = (int) ($request->get_param('per_page') ?? 10);
            $post_id = (int) ($request->get_param('post_id') ?? 0);
            $search = $request->get_param('search');
            $orderby = $request->get_param('orderby') ?? 'date';
            $order = strtoupper($request->get_param('order') ?? 'DESC');
            $status_param = $request->get_param('status') ?? 'approve';

            // Map frontend status values to WordPress status values
            $status_map = [
                'approved' => 'approve',
                'pending' => 'hold',
                'all' => 'all',
            ];
            $status = isset($status_map[$status_param]) ? $status_map[$status_param] : $status_param;

            $args = [
                'number' => $per_page,
                'offset' => ($page - 1) * $per_page,
                'orderby' => $orderby,
                'order' => $order,
                'status' => $status,
                'search' => $search,
                // Bulk-prime the post cache for the comments' posts so the
                // get_post()/get_permalink() calls in formatComment() do not
                // trigger one DB query per comment (N+1).
                'update_comment_post_cache' => true,
            ];

            // If post_id is provided, filter comments by post
            if ($post_id > 0) {
                $args['post_id'] = $post_id;
            }

            // Get comments (array of WP_Comment)
            $comments = \get_comments($args);

            // Bulk-prime author user caches so get_userdata() in formatComment()
            // does not run one query per registered-user comment (N+1).
            if (!empty($comments) && \function_exists('cache_users')) {
                $author_ids = [];
                foreach ($comments as $comment) {
                    if (!empty($comment->user_id)) {
                        $author_ids[] = (int) $comment->user_id;
                    }
                }
                if (!empty($author_ids)) {
                    \cache_users(array_values(array_unique($author_ids)));
                }
            }

            $comments = array_map([$this, 'formatComment'], $comments);

            // Get total comments for pagination (memoized per request)
            static $totalMemo = [];
            $total_args = $args;
            unset($total_args['number']);
            unset($total_args['offset']);
            $total_args['count'] = true;
            $key = md5(json_encode($total_args));
            if (!isset($totalMemo[$key])) {
                $totalMemo[$key] = (int) \get_comments($total_args);
            }
            $total = $totalMemo[$key];

            return ResponseService::paginate($comments, [
                'current_page' => $page,
                'per_page' => $per_page,
                'total' => $total,
                'total_pages' => ceil($total / $per_page)
            ]);

        } catch (\Exception $e) {
            return ResponseService::error('Failed to fetch comments: ' . $e->getMessage());
        }
    }

    /**
     * Get a single comment by ID
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function show($request)
    {
        try {
            $comment_id = (int) $request->get_param('id');
            $comment = \get_comment($comment_id);

            if (!$comment) {
                return ResponseService::error(self::MSG_COMMENT_NOT_FOUND, null, 404);
            }

            return ResponseService::success($this->formatComment($comment));

        } catch (\Exception $e) {
            return ResponseService::error('Failed to fetch comment: ' . $e->getMessage());
        }
    }

    /**
     * Create a new comment
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function store($request)
    {
        $result = null;
        try {
            $comment_data = $this->buildStoreCommentData($request);
            if ($comment_data instanceof \WP_REST_Response) {
                $result = $comment_data;
            } else {
                $comment_id = \function_exists('wp_insert_comment') ? (int) \call_user_func('wp_insert_comment', $comment_data) : 0;
                if ($comment_id === 0) {
                    $result = ResponseService::error('Failed to create comment', null, 500);
                } else {
                    $comment = \get_comment($comment_id);
                    $result = ResponseService::success($this->formatComment($comment), 'Comment created successfully', 201);
                }
            }
        } catch (\Exception $e) {
            $result = ResponseService::error('Failed to create comment: ' . $e->getMessage());
        }
        return $result;
    }

    /**
     * Build comment data for store, or return error response
     *
     * @param \WP_REST_Request $request
     * @return array|\WP_REST_Response
     */
    private function buildStoreCommentData($request)
    {
        $result = null;
        $post_id = (int) $request->get_param('post_id');
        $post = \get_post($post_id);
        if (!$post) {
            $result = ResponseService::error('Post not found', null, 404);
        } elseif (!(\function_exists('comments_open') ? (bool) \call_user_func('comments_open', $post_id) : true)) {
            $result = ResponseService::error('Comments are closed for this post', null, 403);
        } else {
            $parent = (int) ($request->get_param('parent') ?? 0);
            $comment_data = [
                'comment_post_ID' => $post_id,
                'comment_content' => $request->get_param('content'),
                'comment_parent' => $parent,
                'comment_approved' => 0,
                'comment_agent' => 'FleekDash API'
            ];
            $author_result = $this->resolveStoreAuthorData($request, $comment_data);
            $result = ($author_result instanceof \WP_REST_Response) ? $author_result : $comment_data;
        }
        return $result;
    }

    /**
     * Resolve author data for store (user or guest)
     *
     * @param \WP_REST_Request $request
     * @param array $comment_data
     * @return \WP_REST_Response|null
     */
    private function resolveStoreAuthorData($request, array &$comment_data)
    {
        if (\is_user_logged_in()) {
            $user = \wp_get_current_user();
            $comment_data['user_id'] = $this->getUserIdFromUser($user);
            $comment_data['comment_author'] = $this->getUserProp($user, 'display_name');
            $comment_data['comment_author_email'] = $this->getUserProp($user, 'user_email');
            $comment_data['comment_author_url'] = $this->getUserProp($user, 'user_url');
            return null;
        }

        $author_name = $request->get_param('author_name');
        $author_email = $request->get_param('author_email');
        if (empty($author_name) || empty($author_email)) {
            return ResponseService::error('Author name and email are required for guest comments', null, 400);
        }

        $comment_data['comment_author'] = $author_name;
        $comment_data['comment_author_email'] = $author_email;
        $author_url = $request->get_param('author_url');
        $comment_data['comment_author_url'] = !empty($author_url) ? $author_url : ($comment_data['comment_author_url'] ?? '');
        return null;
    }

    /**
     * @param object|null $user
     * @return int
     */
    private function getUserIdFromUser($user)
    {
        return (is_object($user) && isset($user->ID)) ? (int) $user->ID : (int) \get_current_user_id();
    }

    /**
     * @param object|null $user
     * @param string $prop
     * @return string
     */
    private function getUserProp($user, $prop)
    {
        return (is_object($user) && isset($user->{$prop})) ? (string) $user->{$prop} : '';
    }

    /**
     * Update an existing comment
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function update($request)
    {
        $result = null;
        try {
            $comment_id = (int) $request->get_param('id');
            $comment = \get_comment($comment_id);

            if (!$comment) {
                $result = ResponseService::error(self::MSG_COMMENT_NOT_FOUND, null, 404);
            } elseif (!\current_user_can('edit_comment', $comment_id)) {
                $result = ResponseService::error('You do not have permission to edit this comment', null, 403);
            } else {
                $result = $this->applyCommentUpdate($request, $comment_id);
            }
        } catch (\Exception $e) {
            $result = ResponseService::error('Failed to update comment: ' . $e->getMessage());
        }
        return $result;
    }

    /**
     * Apply content, date, and status updates to a comment
     *
     * @param \WP_REST_Request $request
     * @param int $comment_id
     * @return \WP_REST_Response
     */
    private function applyCommentUpdate($request, $comment_id)
    {
        $result = null;
        $comment_data = ['comment_ID' => $comment_id];

        if ($request->get_param('content') !== null) {
            $comment_data['comment_content'] = $request->get_param('content');
        }

        $date_result = $this->parseAndApplyDate($request, $comment_data);
        if ($date_result !== null) {
            $result = $date_result;
        } else {
            $status_result = $this->applyStatusUpdate($request, $comment_id, $comment_data);
            if ($status_result !== null) {
                $result = $status_result;
            } elseif (count($comment_data) > 1) {
                $updated = \wp_update_comment($comment_data);
                $result = ($updated || $updated === 0)
                    ? ResponseService::success($this->formatComment(\get_comment($comment_id)), 'Comment updated successfully')
                    : ResponseService::error('Failed to update comment', null, 500);
            } else {
                $result = ResponseService::success($this->formatComment(\get_comment($comment_id)), 'Comment updated successfully');
            }
        }
        return $result;
    }

    /**
     * Parse date param and add to comment_data
     *
     * @param \WP_REST_Request $request
     * @param array $comment_data
     * @return \WP_REST_Response|null Error response or null if ok
     */
    private function parseAndApplyDate($request, array &$comment_data)
    {
        $date = $request->get_param('date');
        if ($date === null) {
            return null;
        }
        $timestamp = strtotime($date);
        if ($timestamp === false) {
            return ResponseService::error('Invalid date format. Expected MySQL format (Y-m-d H:i:s) or RFC3339', null, 400);
        }
        if (strpos($date, 'T') !== false) {
            $date = date('Y-m-d H:i:s', $timestamp);
        }
        $comment_data['comment_date'] = $date;
        $comment_data['comment_date_gmt'] = get_gmt_from_date($date);
        return null;
    }

    /**
     * Apply status update from request
     *
     * @param \WP_REST_Request $request
     * @param int $comment_id
     * @param array $comment_data
     * @return \WP_REST_Response|null Success/error response or null if no status update
     */
    private function applyStatusUpdate($request, $comment_id, array &$comment_data)
    {
        $status = $request->get_param('status');
        $result = null;
        if ($status !== null) {
            if (!\current_user_can('moderate_comments')) {
                $result = ResponseService::error('You do not have permission to moderate comments', null, 403);
            } else {
                $approval_map = [
                    'approve' => 1, 'approved' => 1,
                    'hold' => 0, 'pending' => 0,
                    'spam' => 'spam'
                ];
                if (isset($approval_map[$status])) {
                    $comment_data['comment_approved'] = $approval_map[$status];
                } elseif ($status === 'trash') {
                    $result = $this->trashCommentWithUpdates($comment_id, $comment_data);
                } else {
                    $result = ResponseService::error("Invalid comment status: '{$status}'. Valid values: approve, approved, hold, pending, spam, trash", null, 400);
                }
            }
        }
        return $result;
    }

    /**
     * Trash a comment, applying content/date changes first
     *
     * @param int $comment_id
     * @param array $comment_data
     * @return \WP_REST_Response
     */
    private function trashCommentWithUpdates($comment_id, array $comment_data)
    {
        if (count($comment_data) > 1) {
            \wp_update_comment($comment_data);
        }
        if (\function_exists('wp_trash_comment')) {
            \call_user_func('wp_trash_comment', $comment_id);
        }
        if (\function_exists('clean_comment_cache')) {
            \clean_comment_cache($comment_id);
        }
        $fresh_comment = \get_comment($comment_id);
        return ResponseService::success($this->formatComment($fresh_comment), 'Comment trashed successfully');
    }

    /**
     * Get comment statistics
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function getStats(\WP_REST_Request $_request) // NOSONAR php:S1172 - WP REST callback signature
    {
        try {
            global $wpdb;

            // Get counts by status
            $stats = $wpdb->get_results(
                "SELECT comment_approved as status, COUNT(*) as count
                 FROM {$wpdb->comments}
                 WHERE comment_type IN ('', 'comment')
                 GROUP BY comment_approved",
                ARRAY_A
            );

            $formatted_stats = [
                'total' => 0,
                'approved' => 0,
                'pending' => 0,
                'spam' => 0,
                'trash' => 0,
            ];

            foreach ($stats as $stat) {
                $count = (int) $stat['count'];
                $formatted_stats['total'] += $count;

                $status = $stat['status'];
                if ($status === '1' || $status === 'approve') {
                    $formatted_stats['approved'] += $count;
                } elseif ($status === '0' || $status === 'hold') {
                    $formatted_stats['pending'] += $count;
                } elseif ($status === 'spam') {
                    $formatted_stats['spam'] += $count;
                } elseif ($status === 'trash') {
                    $formatted_stats['trash'] += $count;
                }
            }

            $labels = [
                'approved' => 'Approved',
                'pending' => 'Pending',
                'spam' => 'Spam',
                'trash' => 'Trash',
            ];

            $statusOptions = [
                ['value' => 'approved', 'label' => 'Approved'],
                ['value' => 'pending', 'label' => 'Pending'],
                ['value' => 'spam', 'label' => 'Spam'],
                ['value' => 'trash', 'label' => 'Trash'],
            ];

            return ResponseService::success([
                'stats' => $formatted_stats,
                'labels' => $labels,
                'statusOptions' => $statusOptions,
            ]);

        } catch (\Exception $e) {
            return ResponseService::error('Failed to fetch comment statistics: ' . $e->getMessage());
        }
    }

    /**
     * Get comments metadata (authors, posts, etc)
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function getMetadata(\WP_REST_Request $_request) // NOSONAR php:S1172 - WP REST callback signature
    {
        try {
            global $wpdb;

            // Get unique comment authors
            $authors = $wpdb->get_results(
                "SELECT DISTINCT comment_author, comment_author_email, user_id
                 FROM {$wpdb->comments}
                 WHERE comment_type IN ('', 'comment')
                 AND comment_approved != 'spam'
                 ORDER BY comment_author ASC",
                ARRAY_A
            );

            $formatted_authors = [];
            foreach ($authors as $author) {
                $user_id = $author['user_id'] ? (int) $author['user_id'] : 0;
                $email = $author['comment_author_email'];

                $formatted_authors[] = [
                    'id' => $user_id > 0 ? (string) $user_id : md5(strtolower(trim($email))),
                    'name' => $author['comment_author'],
                    'email' => $email,
                    'avatar_url' => get_avatar_url($email, ['size' => 96]),
                    'username' => $user_id > 0 ? \get_userdata($user_id)->user_login ?? '' : '',
                ];
            }

            // Get posts with comments
            $posts_with_comments = $wpdb->get_results(
                "SELECT DISTINCT comment_post_ID as post_id,
                        COUNT(*) as comment_count
                 FROM {$wpdb->comments}
                 WHERE comment_type IN ('', 'comment')
                 AND comment_approved != 'spam'
                 GROUP BY comment_post_ID
                 ORDER BY comment_count DESC
                 LIMIT 20",
                ARRAY_A
            );

            $formatted_posts = [];
            foreach ($posts_with_comments as $post_data) {
                $post = \get_post($post_data['post_id']);
                if ($post) {
                    $formatted_posts[] = [
                        'id' => $post->ID,
                        'title' => $post->post_title,
                        'comment_count' => (int) $post_data['comment_count'],
                    ];
                }
            }

            return ResponseService::success([
                'authors' => $formatted_authors,
                'posts' => $formatted_posts,
                'tags' => [], // Comments don't have tags by default
                'categories' => [], // Comments don't have categories by default
                'labels' => [], // Custom taxonomies for comments (if any)
            ]);

        } catch (\Exception $e) {
            return ResponseService::error('Failed to fetch comment metadata: ' . $e->getMessage());
        }
    }

    /**
     * Delete a comment
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function destroy($request)
    {
        $result = null;
        try {
            $comment_id = (int) $request->get_param('id');
            $force = (bool) ($request->get_param('force') ?? false);
            error_log("[CommentController::destroy] Attempting to delete comment ID: $comment_id (force: " . ($force ? 'true' : 'false') . ")");

            $comment = \get_comment($comment_id);
            if (!$comment) {
                error_log("[CommentController::destroy] Comment not found: $comment_id");
                $result = ResponseService::error(self::MSG_COMMENT_NOT_FOUND, null, 404);
            } elseif (!\current_user_can('edit_comment', $comment_id)) {
                error_log("[CommentController::destroy] Permission check: NO");
                $result = ResponseService::error('You do not have permission to delete this comment', null, 403);
            } else {
                $deleted = $this->performCommentDeletion($comment_id, $comment->comment_approved, $force);
                $result = $deleted
                    ? ResponseService::success(null, 'Comment deleted successfully')
                    : ResponseService::error('Failed to delete comment');
            }
        } catch (\Exception $e) {
            error_log("[CommentController::destroy] Exception: " . $e->getMessage());
            $result = ResponseService::error('Failed to delete comment: ' . $e->getMessage());
        }
        return $result;
    }

    /**
     * Perform comment deletion (trash or permanent)
     *
     * @param int $comment_id
     * @param string $approved_status
     * @param bool $force
     * @return bool
     */
    private function performCommentDeletion($comment_id, $approved_status, $force)
    {
        $permanent = ($approved_status === 'trash' || $force);
        if ($permanent) {
            return (bool) \wp_delete_comment($comment_id, true);
        }
        return (bool) \wp_trash_comment($comment_id);
    }

    /**
     * Format a comment for API response
     *
     * @param \WP_Comment $comment
     * @return array
     */
    private function formatComment($comment)
    {
        $author = $this->buildFormatAuthor($comment);
        $status = $this->resolveCommentStatus($comment->comment_approved);
        $post = \get_post($comment->comment_post_ID);
        $post_title = $post ? $post->post_title : 'Unknown Post';
        $post_type = $post ? $post->post_type : 'post';
        $post_url = $post ? get_permalink($post->ID) : '';
        $parent_author = $this->getParentAuthorName($comment);
        $relative_date = \FleekDash\API\Posts\PostsDataFormatter::getRelativeTime($comment->comment_date);
        $link = \function_exists('get_comment_link') ? (string) \call_user_func('get_comment_link', $comment) : '';

        return [
            'id' => (int) $comment->comment_ID,
            'post_id' => (int) $comment->comment_post_ID,
            'post_title' => $post_title,
            'post_type' => $post_type,
            'post_url' => $post_url,
            'parent_id' => (int) $comment->comment_parent,
            'parent_author' => $parent_author,
            'author' => $author,
            'content' => [
                'raw' => $comment->comment_content,
                'rendered' => \apply_filters('comment_text', $comment->comment_content, $comment)
            ],
            'status' => $status,
            'date' => mysql_to_rfc3339($comment->comment_date),
            'relative_date' => $relative_date,
            'created_date' => mysql_to_rfc3339($comment->comment_date),
            'link' => $link,
            'type' => $comment->comment_type ?: 'comment'
        ];
    }

    /**
     * Build author array for formatted comment
     *
     * @param \WP_Comment $comment
     * @return array
     */
    private function buildFormatAuthor($comment)
    {
        if ($comment->user_id) {
            $user = \get_userdata($comment->user_id);
            if ($user) {
                return [
                    'id' => $user->ID,
                    'name' => $user->display_name,
                    'email' => $user->user_email,
                    'avatarUrl' => get_avatar_url($user->ID, ['size' => 96]),
                    'username' => $user->user_login,
                    'user_login' => $user->user_login,
                ];
            }
        }
        return [
            'id' => 0,
            'name' => $comment->comment_author,
            'email' => $comment->comment_author_email,
            'avatarUrl' => get_avatar_url($comment->comment_author_email, ['size' => 96]),
            'username' => '',
            'user_login' => '',
        ];
    }

    /**
     * Resolve comment status from WordPress approval value
     *
     * @param string $approved
     * @return string
     */
    private function resolveCommentStatus($approved)
    {
        $map = ['1' => 'approved', 'spam' => 'spam', 'trash' => 'trash'];
        return $map[$approved] ?? 'pending';
    }

    /**
     * Get parent comment author name if reply
     *
     * @param \WP_Comment $comment
     * @return string
     */
    private function getParentAuthorName($comment)
    {
        if ($comment->comment_parent <= 0) {
            return '';
        }
        $parent_comment = \get_comment($comment->comment_parent);
        if (!$parent_comment || !isset($parent_comment->comment_author)) {
            return '';
        }
        $author_name = trim((string) $parent_comment->comment_author);
        $invalid = ['', '0', 'null', 'undefined'];
        return in_array($author_name, $invalid, true) ? '' : $author_name;
    }
}
