<?php

namespace FleekDash\Core;

use FleekDash\Utils\AvatarHelper;

/**
 * Avatar manager: optional initials-based avatars when no photo is available.
 *
 * Kept as a small core service (not a full module). Replaces generic Gravatar
 * placeholders with deterministic initials when appropriate.
 */
class AvatarManager
{
    /**
     * Singleton instance.
     */
    private static ?self $instance = null;

    /**
     * In-request cache for Gravatar URL probes.
     *
     * @var array<string, bool>
     */
    private array $gravatarCache = [];

    /**
     * Return the singleton instance.
     */
    public static function getInstance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Register WordPress avatar filters.
     */
    public function init()
    {
        // Use high priority to override theme and other plugin avatar handlers
        \add_filter('get_avatar', [$this, 'customAvatar'], 999, 5);
        \add_filter('get_avatar_url', [$this, 'customAvatarUrl'], 999, 3);
    }

    /**
     * Filter: build custom avatar HTML (initials or uploaded URL).
     */
    public function customAvatar(mixed $avatar, mixed $id_or_email, int $size, mixed $default, string $alt): string
    {
        unset($default);
        $result_avatar = (string) $avatar;
        $user = $this->getUserFromIdOrEmail($id_or_email);
        if ($user) {
            // PRIORITY 1: FleekDash / legacy avatars via attachment ID (offload/CDN-friendly)
            $custom_avatar_url = AvatarHelper::resolveUserAvatarUrl($user->ID);
            if (!empty($custom_avatar_url)) {
                $result_avatar = $this->buildAvatarImgTag($alt, $custom_avatar_url, $size);
            } elseif (strpos((string) $avatar, 'gravatar') !== false || strpos((string) $avatar, 'mystery') !== false) {
                // PRIORITY 2: Try Gravatar direct URL before generating initials
                $gravatar_url = $this->getGravatarUrl($user, $size);
                if (!empty($gravatar_url)) {
                    $result_avatar = $this->buildAvatarImgTag($alt, $gravatar_url, $size);
                } else {
                    // PRIORITY 3: Generate avatar with initials as fallback
                    $initials = $this->getUserInitials($user);
                    $avatar_url = $this->generateAvatarUrl($initials, $size);
                    $result_avatar = $this->buildAvatarImgTag($alt, $avatar_url, $size);
                }
            }
        }

        return $result_avatar;
    }

    /**
     * Filter: resolve custom avatar URL.
     */
    public function customAvatarUrl(mixed $url, mixed $id_or_email, array $args): string
    {
        $resolved_url = (string) $url;
        $user = $this->getUserFromIdOrEmail($id_or_email);
        if ($user) {
            // PRIORITY 1: FleekDash / legacy avatars via attachment ID (offload/CDN-friendly)
            $custom_avatar_url = AvatarHelper::resolveUserAvatarUrl($user->ID);
            if (!empty($custom_avatar_url)) {
                $resolved_url = $custom_avatar_url;
            } elseif (strpos((string) $url, 'gravatar') !== false || strpos((string) $url, 'mystery') !== false) {
                $size = isset($args['size']) ? (int) $args['size'] : 96;

                // PRIORITY 3: Try Gravatar direct URL before generating initials
                $gravatar_url = $this->getGravatarUrl($user, $size);
                if (!empty($gravatar_url)) {
                    $resolved_url = $gravatar_url;
                } else {
                    // PRIORITY 4: Generate avatar with initials as fallback
                    $initials = $this->getUserInitials($user);
                    $resolved_url = $this->generateAvatarUrl($initials, $size);
                }
            }
        }

        return $resolved_url;
    }

    /**
     * Resolve a WP_User from an ID, email, or comment object.
     */
    private function getUserFromIdOrEmail(mixed $id_or_email): \WP_User|false
    {
        $user = false;

        if (is_numeric($id_or_email)) {
            $resolved_user = \get_user_by('id', $id_or_email);
            if ($resolved_user instanceof \WP_User) {
                $user = $resolved_user;
            }
        } elseif (is_string($id_or_email) && \is_email($id_or_email)) {
            $resolved_user = \get_user_by('email', $id_or_email);
            if ($resolved_user instanceof \WP_User) {
                $user = $resolved_user;
            }
        } elseif (is_object($id_or_email) && isset($id_or_email->user_id)) {
            $resolved_user = \get_user_by('id', $id_or_email->user_id);
            if ($resolved_user instanceof \WP_User) {
                $user = $resolved_user;
            }
        }

        return $user;
    }

    /**
     * Derive up to two initials from display name fields
     */
    private function getUserInitials(\WP_User $user): string
    {
        $name = '';

        // Prefer first + last, then display name, nicename, login
        if (!empty($user->first_name) && !empty($user->last_name)) {
            $name = $user->first_name . ' ' . $user->last_name;
        } elseif (!empty($user->display_name)) {
            $name = $user->display_name;
        } elseif (!empty($user->user_nicename)) {
            $name = $user->user_nicename;
        } else {
            $name = $user->user_login;
        }

        // Take up to two initials from words
        $words = explode(' ', trim($name));
        $initials = '';

        foreach ($words as $word) {
            if (!empty($word)) {
                $initials .= strtoupper(substr($word, 0, 1));
            }
            if (strlen($initials) >= 2) {
                break; // Cap at two initials
            }
        }

        return $initials ?: 'U'; // Fallback letter when empty
    }

    /**
     * Background palette for initials avatars.
     * Palette tuned for contrast with white label text.
     *
     * @return string[]
     */
    private function getAvatarPalette()
    {
        return [
            '#4F46E5', // Indigo
            '#059669', // Emerald
            '#E11D48', // Rose
            '#B45309', // Amber
            '#8B5CF6', // Violet
            '#0E7490', // Cyan
            '#475569', // Slate
            '#C2410C', // Orange
            '#2563EB', // Blue
            '#DB2777', // Pink
        ];
    }

    /**
     * Encode initials avatar as a data: URL.
     */
    private function generateAvatarUrl(string $initials, int $size): string
    {
        $colors = $this->getAvatarPalette();
        $color_index = array_sum(array_map('ord', str_split($initials))) % count($colors);
        $bg_color = $colors[$color_index];

        // Build SVG markup
        $svg = $this->generateAvatarSvg($initials, $bg_color, $size);

        // Encode as inline data URL
        return 'data:image/svg+xml;base64,' . base64_encode($svg);
    }

    /**
     * Build minimal SVG markup for the initials badge.
     */
    private function generateAvatarSvg(string $initials, string $bg_color, int $size): string
    {
        $font_size = $size * 0.4; // 40% of avatar size

        return sprintf(
            '<svg width="%d" height="%d" viewBox="0 0 %d %d" xmlns="http://www.w3.org/2000/svg">
                <rect width="%d" height="%d" fill="%s"/>
                <text x="50%%" y="50%%" text-anchor="middle" dy="0.35em" font-family="Arial, sans-serif" font-size="%d" font-weight="bold" fill="white">%s</text>
            </svg>',
            $size,
            $size,
            $size,
            $size,
            $size,
            $size,
            $bg_color,
            $font_size,
            \esc_html($initials),
        );
    }

    /**
     * Deterministic background color for a user id (palette index).
     */
    public function getUserAvatarColor(int $user_id): string
    {
        $user = \get_user_by('id', $user_id);
        if (!$user) {
            return '#475569'; // Fallback Slate
        }

        $initials = $this->getUserInitials($user);
        $colors = $this->getAvatarPalette();

        $color_index = array_sum(array_map('ord', str_split($initials))) % count($colors);
        return $colors[$color_index];
    }

    /**
     * Resolve a Gravatar HTTPS URL when the user has a real photo.
     *
     * @param \WP_User $user
     * @param int      $size
     *
     * @return string|null
     */
    private function getGravatarUrl(\WP_User $user, int $size): ?string
    {
        $email = isset($user->user_email) ? strtolower(trim((string) $user->user_email)) : '';
        if ($email === '') {
            return null;
        }

        $hash = md5($email);
        if (!$this->gravatarExists($hash)) {
            return null;
        }

        // Omit `d=404` — that param is only for the HEAD probe in gravatarExists().
        // Including it here makes EnhancedAvatar treat verified photos as placeholders.
        $query = http_build_query(
            [
                's' => (int) $size,
                'r' => 'g',
            ]
        );

        return sprintf('https://www.gravatar.com/avatar/%s?%s', $hash, $query);
    }

    /**
     * HEAD-check Gravatar 404 with persistent cache to avoid repeat HTTP calls
     *
     * @param string $hash
     *
     * @return bool
     */
    private function gravatarExists(string $hash): bool
    {
        if (isset($this->gravatarCache[$hash])) {
            return $this->gravatarCache[$hash];
        }

        $cache_key = 'fleekdash_gravatar_' . $hash;
        $cached = wp_cache_get($cache_key, 'fleekdash_avatar');
        if ($cached !== false) {
            $this->gravatarCache[$hash] = (bool) $cached;
            return $this->gravatarCache[$hash];
        }

        $response = wp_remote_head('https://www.gravatar.com/avatar/' . $hash . '?d=404');
        $exists = false;

        if (!is_wp_error($response)) {
            $status_code = (int) wp_remote_retrieve_response_code($response);
            $exists = $status_code >= 200 && $status_code < 300;
        }

        wp_cache_set($cache_key, $exists ? 1 : 0, 'fleekdash_avatar', DAY_IN_SECONDS);
        $this->gravatarCache[$hash] = $exists;

        return $exists;
    }

    /**
     * Build the avatar <img> tag.
     */
    private function buildAvatarImgTag(string $alt, string $url, int $size): string
    {
        return sprintf(
            '<img alt="%s" src="%s" class="avatar avatar-%d photo fleekdash-avatar" height="%d" width="%d" />',
            \esc_attr($alt),
            \esc_url($url),
            (int) $size,
            (int) $size,
            (int) $size
        );
    }
}
