<?php

namespace FleekDash\Utils;

/**
 * Avatar Helper
 *
 * Centralized avatar URL fetching that is INDEPENDENT of theme avatar systems.
 * Ensures FleekDash always gets the correct avatar regardless of active theme.
 *
 * @package FleekDash\Utils
 * @since 2.2.0
 */
class AvatarHelper
{
    private const FLEEKDASH_AVATAR_ID_META = '_fleekdash_avatar_id';
    private const FLEEKDASH_AVATAR_URL_META = '_fleekdash_avatar_url';

    /**
     * Get user avatar URL with theme-independent fallback chain
     *
     * Priority:
     * 1. FleekDash custom avatar: prefer attachment ID so wp_get_attachment_url() is used
     *    (offload/CDN plugins filter that; raw URL in meta may be local-only).
     * 2. FleekDash custom avatar URL (when no ID, e.g. external or legacy data).
     * 3. Legacy avatar plugins (Simple Local Avatars, WP User Avatar, etc)
     * 4. WordPress native get_avatar_url() as last resort
     *
     * @param int $user_id User ID
     * @param array $args Optional. Avatar args (size, default, etc)
     * @return string Avatar URL
     */
    public static function getUserAvatarUrl($user_id, $args = [])
    {
        $size = $args['size'] ?? 96;
        $url = self::resolveUserAvatarUrl($user_id);

        return $url ?: \get_avatar_url($user_id, ['size' => $size]);
    }

    /**
     * Resolve avatar URL from FleekDash meta and legacy plugins only.
     *
     * Does not call get_avatar_url() — safe for AvatarManager filters without recursion.
     * Uses wp_get_attachment_url() where possible so offload/CDN plugins can filter URLs.
     *
     * @param int $user_id User ID
     * @return string|null Avatar URL or null when no custom avatar is stored
     */
    public static function resolveUserAvatarUrl($user_id)
    {
        return self::tryFleekdashAvatarById($user_id)
            ?? self::tryFleekdashAvatarByUrl($user_id)
            ?? self::tryLegacyAvatar($user_id);
    }

    /**
     * Get user avatar URL for multiple users (bulk operation)
     *
     * More efficient than calling getUserAvatarUrl() in a loop
     * as it pre-fetches all user meta in a single query.
     *
     * @param array $user_ids Array of user IDs
     * @param array $args Optional. Avatar args (size, default, etc)
     * @return array Associative array [user_id => avatar_url]
     */
    public static function getUsersAvatarUrls($user_ids, $args = [])
    {
        if (empty($user_ids)) {
            return [];
        }

        $size = $args['size'] ?? 96;
        $meta_keys = self::getAvatarMetaKeys();
        $user_meta = self::fetchAvatarMetaByUserIds($user_ids, $meta_keys);
        $avatars = [];

        foreach ($user_ids as $user_id) {
            $user_id = (int) $user_id;
            $meta = $user_meta[$user_id] ?? [];
            $avatars[$user_id] = self::resolveAvatarUrlFromPrefetchedMeta($user_id, $meta, $meta_keys, $size);
        }

        return $avatars;
    }

    /**
     * Update user avatar URL (FleekDash system)
     *
     * Also syncs with Voxel theme and other avatar systems for compatibility.
     *
     * @param int $user_id User ID
     * @param string $avatar_url Avatar URL
     * @param bool $sync_with_voxel Whether to sync with Voxel theme (default: true)
     * @return bool Success
     */
    public static function updateUserAvatar($user_id, $avatar_url, $sync_with_voxel = true)
    {
        // Update FleekDash avatar URL
        $result = \update_user_meta($user_id, self::FLEEKDASH_AVATAR_URL_META, $avatar_url);

        // Resolve and store attachment ID when URL is a local upload (for offload/CDN compatibility).
        // getUserAvatarUrl() will prefer wp_get_attachment_url($id), which plugins can filter.
        $attachment_id = AvatarOffloadResolver::getAttachmentIdFromUrl($avatar_url);
        if ($attachment_id) {
            \update_user_meta($user_id, self::FLEEKDASH_AVATAR_ID_META, $attachment_id);
        } else {
            \delete_user_meta($user_id, self::FLEEKDASH_AVATAR_ID_META);
        }

        // COMPATIBILITY: Sync with Voxel theme if active
        if ($sync_with_voxel && self::isVoxelActive()) {
            if ($attachment_id) {
                \update_user_meta($user_id, 'voxel:avatar', $attachment_id);
            } else {
                \update_user_meta($user_id, 'voxel:avatar', $avatar_url);
            }
        }

        // Clear WordPress user cache to force fresh avatar
        \wp_cache_delete($user_id, 'users');
        \wp_cache_delete($user_id, 'user_meta');
        \clean_user_cache($user_id);

        return $result !== false;
    }

    /**
     * Delete user avatar (FleekDash system)
     *
     * Also removes from Voxel for compatibility.
     *
     * @param int $user_id User ID
     * @param bool $sync_with_voxel Whether to sync with Voxel theme (default: true)
     * @return bool Success
     */
    public static function deleteUserAvatar($user_id, $sync_with_voxel = true)
    {
        $result = \delete_user_meta($user_id, self::FLEEKDASH_AVATAR_URL_META);

        // COMPATIBILITY: Also remove from Voxel
        if ($sync_with_voxel && self::isVoxelActive()) {
            \delete_user_meta($user_id, 'voxel:avatar');
            \delete_user_meta($user_id, self::FLEEKDASH_AVATAR_ID_META);
        }

        // Clear WordPress user cache
        \wp_cache_delete($user_id, 'users');
        \wp_cache_delete($user_id, 'user_meta');
        \clean_user_cache($user_id);

        return $result;
    }

    /**
     * Try to get FleekDash avatar via attachment ID (offload-friendly)
     *
     * @param int $user_id User ID
     * @return string|null Avatar URL or null if not found
     */
    private static function tryFleekdashAvatarById($user_id)
    {
        $result = null;
        $fleekdash_avatar_id = \get_user_meta($user_id, self::FLEEKDASH_AVATAR_ID_META, true);
        $has_valid_id = !empty($fleekdash_avatar_id) && is_numeric($fleekdash_avatar_id);

        if ($has_valid_id) {
            $result = AvatarOffloadResolver::resolveAttachmentIdToUrl((int) $fleekdash_avatar_id);
        }

        return $result;
    }

    /**
     * Try to get FleekDash avatar via stored URL; resolve to attachment ID when possible
     *
     * @param int $user_id User ID
     * @return string|null Avatar URL or null if not found
     */
    private static function tryFleekdashAvatarByUrl($user_id)
    {
        $fleekdash_avatar = \get_user_meta($user_id, self::FLEEKDASH_AVATAR_URL_META, true);
        if (empty($fleekdash_avatar)) {
            return null;
        }

        return AvatarOffloadResolver::resolveStoredUrl(
            $fleekdash_avatar,
            $user_id,
            self::FLEEKDASH_AVATAR_ID_META
        );
    }

    /**
     * Try to get avatar from legacy plugins (Voxel, Simple Local Avatars, etc)
     *
     * @param int $user_id User ID
     * @return string|null Avatar URL or null if not found
     */
    private static function tryLegacyAvatar($user_id)
    {
        foreach (self::getLegacyAvatarMetaKeys() as $key) {
            $legacy_avatar = \get_user_meta($user_id, $key, true);
            if (empty($legacy_avatar)) {
                continue;
            }

            $resolved = self::resolveLegacyAvatarValue($legacy_avatar);
            if ($resolved !== null) {
                return $resolved;
            }
        }

        return null;
    }

    /**
     * @return string[]
     */
    private static function getAvatarMetaKeys()
    {
        return array_merge(
            [self::FLEEKDASH_AVATAR_ID_META, self::FLEEKDASH_AVATAR_URL_META],
            self::getLegacyAvatarMetaKeys()
        );
    }

    /**
     * @return string[]
     */
    private static function getLegacyAvatarMetaKeys()
    {
        static $keys = null;

        if ($keys === null) {
            $keys = [
                'voxel:avatar',
                'simple_local_avatar',
                'wp_user_avatar',
                'avatar_manager_avatar',
                'basic_user_avatar',
                'custom_avatar',
            ];
        }

        return $keys;
    }

    /**
     * @param array<int|string> $user_ids
     * @param string[] $meta_keys
     * @return array<int, array<string, string>>
     */
    private static function fetchAvatarMetaByUserIds($user_ids, $meta_keys)
    {
        global $wpdb;

        $user_ids_str = implode(',', array_map('intval', $user_ids));
        $meta_keys_in = "'" . implode("','", array_map('esc_sql', $meta_keys)) . "'";

        $results = $wpdb->get_results(
            "SELECT user_id, meta_key, meta_value
            FROM {$wpdb->usermeta}
            WHERE user_id IN ($user_ids_str)
            AND meta_key IN ($meta_keys_in)",
            ARRAY_A
        );

        $user_meta = [];
        foreach ($results as $row) {
            $user_id = (int) $row['user_id'];
            $user_meta[$user_id][$row['meta_key']] = $row['meta_value'];
        }

        return $user_meta;
    }

    /**
     * @param array<string, string> $meta
     * @param string[] $meta_keys
     */
    private static function resolveAvatarUrlFromPrefetchedMeta(int $user_id, array $meta, array $meta_keys, int $size): string
    {
        $fleekdash_url = self::resolveFleekdashAvatarFromMeta($user_id, $meta);
        if ($fleekdash_url !== null) {
            return $fleekdash_url;
        }

        $legacy_url = self::resolveLegacyAvatarFromMeta($meta, $meta_keys);
        if ($legacy_url !== null) {
            return $legacy_url;
        }

        return \get_avatar_url($user_id, ['size' => $size]);
    }

    /**
     * @param array<string, string> $meta
     */
    private static function resolveFleekdashAvatarFromMeta(int $user_id, array $meta): ?string
    {
        $result = null;
        $id_meta = $meta[self::FLEEKDASH_AVATAR_ID_META] ?? null;
        $url_meta = $meta[self::FLEEKDASH_AVATAR_URL_META] ?? '';

        if (!empty($id_meta) && is_numeric($id_meta)) {
            $avatar_url = \wp_get_attachment_url((int) $id_meta);
            if ($avatar_url) {
                $offloaded_local = AvatarOffloadResolver::isUsableAvatarUrl($avatar_url) === false;
                $result = $offloaded_local
                    ? AvatarOffloadResolver::resolveStoredUrl($url_meta, $user_id, self::FLEEKDASH_AVATAR_ID_META)
                    : $avatar_url;
            }
        }

        if ($result === null && !empty($url_meta)) {
            $result = AvatarOffloadResolver::resolveStoredUrl(
                $url_meta,
                $user_id,
                self::FLEEKDASH_AVATAR_ID_META
            );
        }

        return $result;
    }

    /**
     * @param array<string, string> $meta
     * @param string[] $meta_keys
     */
    private static function resolveLegacyAvatarFromMeta($meta, $meta_keys)
    {
        foreach ($meta_keys as $key) {
            if ($key === self::FLEEKDASH_AVATAR_URL_META || $key === self::FLEEKDASH_AVATAR_ID_META) {
                continue;
            }

            if (empty($meta[$key])) {
                continue;
            }

            $resolved = self::resolveLegacyAvatarValue($meta[$key]);
            if ($resolved !== null) {
                return $resolved;
            }
        }

        return null;
    }

    /**
     * @param mixed $legacy_avatar
     */
    private static function resolveLegacyAvatarValue($legacy_avatar)
    {
        $result = null;

        if (is_numeric($legacy_avatar)) {
            $result = AvatarOffloadResolver::resolveAttachmentIdToUrl((int) $legacy_avatar);
        } elseif (is_string($legacy_avatar) && $legacy_avatar !== '') {
            $result = AvatarOffloadResolver::resolveStoredUrl($legacy_avatar);
        }

        return $result;
    }

    /**
     * Check if Voxel theme is active
     *
     * @return bool True if Voxel theme is active
     */
    private static function isVoxelActive()
    {
        static $is_active = null;

        if ($is_active === null) {
            $theme = \wp_get_theme();
            $is_active = (
                $theme->get('Name') === 'Voxel' ||
                $theme->get('Template') === 'voxel' ||
                $theme->get('TextDomain') === 'voxel'
            );
        }

        return $is_active;
    }
}
