<?php

namespace FleekDash\Controllers\Analytics;

use FleekDash\Services\ResponseService;
use FleekDash\Controllers\Analytics\Traits\HasDateRangeHelpers;
use FleekDash\Utils\AvatarHelper;
use WP_REST_Request;

/**
 * Users Stats Controller (V2.2)
 *
 * Analytics endpoints for users with chart data, summaries, and period comparisons.
 * Migrated from includes/API/real/Stats/UsersController.php
 *
 * @package FleekDash\Controllers\Analytics
 * @since 2.2.0
 */
class UsersStatsController
{
    use HasDateRangeHelpers;

    /** @var array<int, int> */
    private array $postsCountMemo = [];

    /** @var array<int, int> */
    private array $commentsCountMemo = [];

    /**
     * Get users stats with date range filtering and pagination
     *
     * @param WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function index($request)
    {
        $range_info = $this->get_date_range_info($request);
        $page = \max(1, \absint($request->get_param('page')) ?: 1);
        $per_page = \min(100, \max(1, \absint($request->get_param('per_page')) ?: 20));

        $data = $this->getRealUsersData($range_info, $page, $per_page);

        return ResponseService::success($data['users'], [
            'total_users' => $data['total_users'],
            'total_pages' => $data['total_pages'],
            'current_page' => $page,
            'per_page' => $per_page,
            'date_range' => $range_info,
            'endpoint' => 'users_full',
        ]);
    }

    /**
     * Get chart data (time series)
     *
     * @param WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function chart($request)
    {
        $range_info = $this->get_date_range_info($request);
        $data = $this->getRealUsersChart($range_info);

        return ResponseService::success($data, [
            'date_range' => $range_info,
            'endpoint' => 'users_chart',
        ]);
    }

    /**
     * Get summary KPIs
     *
     * @param WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function summary($request)
    {
        $range_info = $this->get_date_range_info($request);
        $summary = $this->getRealUsersSummary($range_info);

        return ResponseService::success($summary, [
            'date_range' => $range_info,
            'endpoint' => 'users_summary',
        ]);
    }

    /**
     * Get period comparison data
     *
     * @param WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function comparison($request)
    {
        $range_info = $this->get_date_range_info($request);
        $comparison_type = $request->get_param('comparison_type') ?: 'previous_period';
        $comparison_data = $this->getRealUsersComparison($range_info, (string) $comparison_type);

        return ResponseService::success($comparison_data, [
            'date_range' => $range_info,
            'comparison_type' => $comparison_type,
            'endpoint' => 'users_comparison',
        ]);
    }

    /**
     * @param array<string, mixed> $range_info
     * @return array{users: array<int, array<string, mixed>>, total_users: int, total_pages: float|int}
     */
    private function getRealUsersData(array $range_info, int $page, int $per_page): array
    {
        global $wpdb;

        $date_condition = $this->buildUsersDateCondition($range_info);
        $total_users = (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$wpdb->users} WHERE 1=1 {$date_condition}"
        );

        $offset = ($page - 1) * $per_page;
        $users_query = $wpdb->prepare(
            "SELECT
                u.ID,
                u.user_login,
                u.user_email,
                u.display_name,
                u.user_registered,
                u.user_status,
                um1.meta_value as first_name,
                um2.meta_value as last_name,
                um3.meta_value as description,
                um4.meta_value as website,
                um5.meta_value as locale,
                um6.meta_value as timezone
            FROM {$wpdb->users} u
            LEFT JOIN {$wpdb->usermeta} um1 ON u.ID = um1.user_id AND um1.meta_key = 'first_name'
            LEFT JOIN {$wpdb->usermeta} um2 ON u.ID = um2.user_id AND um2.meta_key = 'last_name'
            LEFT JOIN {$wpdb->usermeta} um3 ON u.ID = um3.user_id AND um3.meta_key = 'description'
            LEFT JOIN {$wpdb->usermeta} um4 ON u.ID = um4.user_id AND um4.meta_key = 'user_url'
            LEFT JOIN {$wpdb->usermeta} um5 ON u.ID = um5.user_id AND um5.meta_key = 'locale'
            LEFT JOIN {$wpdb->usermeta} um6 ON u.ID = um6.user_id AND um6.meta_key = 'timezone_string'
            WHERE 1=1 {$date_condition}
            ORDER BY u.user_registered DESC
            LIMIT %d OFFSET %d",
            $per_page,
            $offset
        );

        $users = $wpdb->get_results($users_query, ARRAY_A);
        $formatted_users = [];

        foreach ($users as $user) {
            $formatted_users[] = $this->formatUserRecord($user);
        }

        return [
            'users' => $formatted_users,
            'total_users' => $total_users,
            'total_pages' => $per_page > 0 ? (int) ceil($total_users / $per_page) : 0,
        ];
    }

    /**
     * @param array<string, mixed> $range_info
     */
    private function buildUsersDateCondition(array $range_info): string
    {
        global $wpdb;

        if (!empty($range_info['start_date']) && !empty($range_info['end_date'])) {
            return $wpdb->prepare(
                'AND DATE(user_registered) BETWEEN %s AND %s',
                $range_info['start_date'],
                $range_info['end_date']
            );
        }

        if (!empty($range_info['days']) && (int) $range_info['days'] > 0) {
            return $wpdb->prepare(
                'AND DATE(user_registered) >= DATE_SUB(NOW(), INTERVAL %d DAY)',
                (int) $range_info['days']
            );
        }

        return '';
    }

    /**
     * @param array<string, mixed> $user
     * @return array<string, mixed>
     */
    private function formatUserRecord(array $user): array
    {
        $user_id = (int) $user['ID'];
        $last_login = \get_user_meta($user_id, 'last_login', true) ?: $user['user_registered'];
        $avatar = AvatarHelper::getUserAvatarUrl($user_id, ['size' => 100]);

        return [
            'id' => $user_id,
            'username' => $user['user_login'],
            'email' => $user['user_email'],
            'display_name' => $user['display_name'],
            'first_name' => $user['first_name'] ?: '',
            'last_name' => $user['last_name'] ?: '',
            'role' => $this->getPrimaryUserRole($user_id),
            'status' => (int) $user['user_status'] === 0 ? 'active' : 'inactive',
            'registration_date' => date('Y-m-d', strtotime($user['user_registered'])),
            'last_login' => date('Y-m-d', strtotime($last_login)),
            'login_count' => (int) \get_user_meta($user_id, 'login_count', true),
            'posts_count' => $this->getMemoizedPostsCount($user_id),
            'comments_count' => $this->getMemoizedCommentsCount($user_id),
            'avatar' => $avatar,
            'bio' => $user['description'] ?: '',
            'website' => $user['website'] ?: '',
            'location' => \get_user_meta($user_id, 'location', true) ?: '',
            'timezone' => $user['timezone'] ?: 'UTC',
            'language' => $user['locale'] ?: 'en_US',
            'meta' => [
                'last_activity' => \get_user_meta($user_id, 'last_activity', true) ?: $user['user_registered'],
                'preferences' => [
                    'email_notifications' => \get_user_meta($user_id, 'email_notifications', true) ?: 'enabled',
                    'two_factor_auth' => \get_user_meta($user_id, 'two_factor_auth', true) ?: 'disabled',
                    'privacy_level' => \get_user_meta($user_id, 'privacy_level', true) ?: 'public',
                ],
            ],
        ];
    }

    private function getPrimaryUserRole(int $user_id): string
    {
        $user_roles = \get_user_meta($user_id, 'wp_capabilities', true);
        if (!$user_roles || !is_array($user_roles)) {
            return 'subscriber';
        }

        $roles = array_keys($user_roles);

        return !empty($roles) ? (string) $roles[0] : 'subscriber';
    }

    private function getMemoizedPostsCount(int $user_id): int
    {
        if (!isset($this->postsCountMemo[$user_id])) {
            $this->postsCountMemo[$user_id] = (int) \count_user_posts($user_id, 'post', true);
        }

        return $this->postsCountMemo[$user_id];
    }

    private function getMemoizedCommentsCount(int $user_id): int
    {
        if (!isset($this->commentsCountMemo[$user_id])) {
            $this->commentsCountMemo[$user_id] = (int) \get_comments([
                'user_id' => $user_id,
                'count' => true,
            ]);
        }

        return $this->commentsCountMemo[$user_id];
    }

    /**
     * @param array<string, mixed> $range_info
     * @return array<int, array<string, mixed>>
     */
    private function getRealUsersChart(array $range_info): array
    {
        global $wpdb;

        [$start_date, $end_date] = $this->resolveChartDateRange($range_info);

        $query = $wpdb->prepare("
            SELECT
                DATE(user_registered) as date,
                COUNT(*) as new_registrations,
                COUNT(DISTINCT u.ID) as total_users,
                COUNT(CASE WHEN um.meta_value >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 END) as active_users,
                COUNT(CASE WHEN um.meta_value >= DATE_SUB(NOW(), INTERVAL 7 DAY) THEN 1 END) as recent_logins
            FROM {$wpdb->users} u
            LEFT JOIN {$wpdb->usermeta} um ON u.ID = um.user_id AND um.meta_key = 'last_login'
            WHERE DATE(user_registered) BETWEEN %s AND %s
            GROUP BY DATE(user_registered)
            ORDER BY date ASC
        ", $start_date, $end_date);

        $results = $wpdb->get_results($query, ARRAY_A);
        $chart_data = [];

        foreach ($results as $row) {
            $total_users = (int) $row['total_users'];
            $active_users = (int) $row['active_users'];
            $chart_data[] = [
                'date' => $row['date'],
                'new_registrations' => (int) $row['new_registrations'],
                'active_users' => $active_users,
                'total_users' => $total_users,
                'login_activity' => (int) $row['recent_logins'],
                'engagement_rate' => $total_users > 0 ? round($active_users / $total_users, 2) : 0,
            ];
        }

        return $chart_data;
    }

    /**
     * @param array<string, mixed> $range_info
     * @return array<string, mixed>
     */
    private function getRealUsersSummary(array $range_info): array
    {
        global $wpdb;

        [$start_date, $end_date] = $this->resolveChartDateRange($range_info);

        $total_users = (int) $wpdb->get_var($wpdb->prepare("
            SELECT COUNT(*) FROM {$wpdb->users}
            WHERE DATE(user_registered) BETWEEN %s AND %s
        ", $start_date, $end_date));

        $active_users = (int) $wpdb->get_var("
            SELECT COUNT(DISTINCT u.ID) FROM {$wpdb->users} u
            LEFT JOIN {$wpdb->usermeta} um ON u.ID = um.user_id AND um.meta_key = 'last_login'
            WHERE um.meta_value >= DATE_SUB(NOW(), INTERVAL 30 DAY)
        ");

        $new_registrations = (int) $wpdb->get_var($wpdb->prepare("
            SELECT COUNT(*) FROM {$wpdb->users}
            WHERE DATE(user_registered) BETWEEN %s AND %s
        ", $start_date, $end_date));

        $top_roles = $wpdb->get_results("
            SELECT
                CASE
                    WHEN um.meta_value LIKE '%administrator%' THEN 'administrator'
                    WHEN um.meta_value LIKE '%editor%' THEN 'editor'
                    WHEN um.meta_value LIKE '%author%' THEN 'author'
                    WHEN um.meta_value LIKE '%contributor%' THEN 'contributor'
                    ELSE 'subscriber'
                END as role,
                COUNT(*) as count
            FROM {$wpdb->users} u
            LEFT JOIN {$wpdb->usermeta} um ON u.ID = um.user_id AND um.meta_key = 'wp_capabilities'
            GROUP BY role
            ORDER BY count DESC
            LIMIT 5
        ", ARRAY_A);

        $last_24h_logins = (int) $wpdb->get_var("
            SELECT COUNT(DISTINCT u.ID) FROM {$wpdb->users} u
            LEFT JOIN {$wpdb->usermeta} um ON u.ID = um.user_id AND um.meta_key = 'last_login'
            WHERE um.meta_value >= DATE_SUB(NOW(), INTERVAL 1 DAY)
        ");

        $last_7d_new_users = (int) $wpdb->get_var("
            SELECT COUNT(*) FROM {$wpdb->users}
            WHERE DATE(user_registered) >= DATE_SUB(NOW(), INTERVAL 7 DAY)
        ");

        $last_30d_active_users = (int) $wpdb->get_var("
            SELECT COUNT(DISTINCT u.ID) FROM {$wpdb->users} u
            LEFT JOIN {$wpdb->usermeta} um ON u.ID = um.user_id AND um.meta_key = 'last_login'
            WHERE um.meta_value >= DATE_SUB(NOW(), INTERVAL 30 DAY)
        ");

        $days = (int) $range_info['days'];

        return [
            'total_users' => $total_users,
            'active_users' => $active_users,
            'new_registrations' => $new_registrations,
            'inactive_users' => $total_users - $active_users,
            'avg_login_frequency' => $active_users > 0 ? round($active_users / max($total_users, 1) * 30, 1) : 0,
            'engagement_rate' => $total_users > 0 ? round($active_users / $total_users, 2) : 0,
            'users_per_day' => $total_users > 0 ? round($total_users / max($days, 1), 1) : 0,
            'registrations_per_day' => $new_registrations > 0 ? round($new_registrations / max($days, 1), 1) : 0,
            'top_roles' => $top_roles,
            'top_locations' => [],
            'recent_activity' => [
                'last_24h_logins' => $last_24h_logins,
                'last_7d_new_users' => $last_7d_new_users,
                'last_30d_active_users' => $last_30d_active_users,
            ],
        ];
    }

    /**
     * @param array<string, mixed> $range_info
     * @return array<string, mixed>
     */
    private function getRealUsersComparison(array $range_info, string $comparison_type): array
    {
        $comparison_dates = $this->get_comparison_dates($range_info, $comparison_type);
        $current_chart_data = $this->getRealUsersChart($range_info);
        $current_summary = $this->getRealUsersSummary($range_info);

        $comparison_range_info = [
            'days' => $comparison_dates['days'],
            'start_date' => $comparison_dates['start_date'],
            'end_date' => $comparison_dates['end_date'],
        ];
        $comparison_chart_data = $this->getRealUsersChart($comparison_range_info);
        $comparison_summary = $this->getRealUsersSummary($comparison_range_info);
        $comparison_metrics = $this->calculateRealUsersComparisonMetrics(
            $current_chart_data,
            $comparison_chart_data
        );

        [$current_start, $current_end] = $this->resolveChartDateRange($range_info);

        return [
            'current_period' => [
                'data' => $current_chart_data,
                'summary' => $current_summary,
            ],
            'comparison_period' => [
                'data' => $comparison_chart_data,
                'summary' => $comparison_summary,
            ],
            'comparison_metrics' => $comparison_metrics,
            'comparison_type' => $comparison_type,
            'period_dates' => [
                'current' => [
                    'start_date' => $current_start,
                    'end_date' => $current_end,
                ],
                'comparison' => [
                    'start_date' => $comparison_dates['start_date'],
                    'end_date' => $comparison_dates['end_date'],
                ],
            ],
        ];
    }

    /**
     * @param array<int, array<string, mixed>> $current_data
     * @param array<int, array<string, mixed>> $comparison_data
     * @return array<string, mixed>
     */
    private function calculateRealUsersComparisonMetrics(array $current_data, array $comparison_data): array
    {
        $current_total_registrations = array_sum(array_column($current_data, 'new_registrations'));
        $comparison_total_registrations = array_sum(array_column($comparison_data, 'new_registrations'));

        $current_total_active = array_sum(array_column($current_data, 'active_users'));
        $comparison_total_active = array_sum(array_column($comparison_data, 'active_users'));

        $current_avg_engagement = array_sum(array_column($current_data, 'engagement_rate')) / max(count($current_data), 1);
        $comparison_avg_engagement = array_sum(array_column($comparison_data, 'engagement_rate')) / max(count($comparison_data), 1);

        $registrations_change = $current_total_registrations - $comparison_total_registrations;
        $registrations_change_percentage = $comparison_total_registrations > 0
            ? ($registrations_change / $comparison_total_registrations) * 100
            : 0;

        $active_change = $current_total_active - $comparison_total_active;
        $active_change_percentage = $comparison_total_active > 0
            ? ($active_change / $comparison_total_active) * 100
            : 0;

        $engagement_change = $current_avg_engagement - $comparison_avg_engagement;
        $engagement_change_percentage = $comparison_avg_engagement > 0
            ? ($engagement_change / $comparison_avg_engagement) * 100
            : 0;

        return [
            'new_registrations' => [
                'current' => $current_total_registrations,
                'previous' => $comparison_total_registrations,
                'change' => $registrations_change,
                'change_percentage' => round($registrations_change_percentage, 2),
                'trend' => $this->getTrendDirection($registrations_change),
            ],
            'active_users' => [
                'current' => $current_total_active,
                'previous' => $comparison_total_active,
                'change' => $active_change,
                'change_percentage' => round($active_change_percentage, 2),
                'trend' => $this->getTrendDirection($active_change),
            ],
            'engagement_rate' => [
                'current' => round($current_avg_engagement * 100, 1),
                'previous' => round($comparison_avg_engagement * 100, 1),
                'change' => round($engagement_change * 100, 1),
                'change_percentage' => round($engagement_change_percentage, 2),
                'trend' => $this->getTrendDirection($engagement_change),
            ],
        ];
    }

    /**
     * @param array<string, mixed> $range_info
     * @return array{0: string, 1: string}
     */
    private function resolveChartDateRange(array $range_info): array
    {
        $days = (int) ($range_info['days'] ?? 30);
        $start_date = !empty($range_info['start_date'])
            ? (string) $range_info['start_date']
            : date('Y-m-d', strtotime("-{$days} days"));
        $end_date = !empty($range_info['end_date'])
            ? (string) $range_info['end_date']
            : date('Y-m-d');

        return [$start_date, $end_date];
    }

    /**
     * @param float|int $change
     */
    private function getTrendDirection($change): string
    {
        if ($change > 0) {
            return 'up';
        }

        if ($change < 0) {
            return 'down';
        }

        return 'stable';
    }
}
