<?php

namespace FleekDash\Utils;

/**
 * Offload/CDN-aware attachment URL resolution for avatars.
 *
 * @package FleekDash\Utils
 * @since 2.6.0
 */
class AvatarOffloadResolver
{
    /**
     * Converts an uploaded media URL to its attachment ID.
     *
     * @return int|false
     */
    public static function getAttachmentIdFromUrl(string $url)
    {
        $attachment_id = false;

        if ($url !== '' && \function_exists('attachment_url_to_postid')) {
            $from_core = (int) \attachment_url_to_postid($url);
            if ($from_core > 0) {
                $attachment_id = $from_core;
            }
        }

        if ($attachment_id === false) {
            $filename = self::extractUploadFilename($url);
            if ($filename !== '') {
                $found = self::lookupAttachmentIdByFilename($filename);
                if ($found !== false) {
                    $attachment_id = $found;
                }
            }
        }

        return $attachment_id;
    }

    /**
     * True when the URL can be rendered (CDN/external, or local file still on disk).
     */
    public static function isUsableAvatarUrl(string $avatar_url): bool
    {
        return $avatar_url !== ''
            && (!self::isLocalUploadUrl($avatar_url) || self::localUploadFileExists($avatar_url));
    }

    /**
     * Resolve attachment ID to a renderable URL (offload plugins may filter wp_get_attachment_url).
     */
    public static function resolveAttachmentIdToUrl(int $attachment_id): ?string
    {
        $avatar_url = \wp_get_attachment_url($attachment_id);
        if (!$avatar_url) {
            $avatar_url = \wp_get_attachment_image_url($attachment_id, 'thumbnail') ?: '';
        }

        return self::isUsableAvatarUrl($avatar_url) ? $avatar_url : null;
    }

    /**
     * Resolve a stored upload URL through attachment ID when possible (offload/CDN-friendly).
     *
     * When resolution fails for a local upload path, returns null so get_avatar / offload
     * plugins can supply the CDN URL.
     *
     * @param mixed $stored_url Stored avatar URL (string expected)
     * @param int|null $user_id
     * @param string|null $persist_id_meta_key When set with $user_id, stores resolved attachment ID
     */
    public static function resolveStoredUrl(mixed $stored_url, ?int $user_id = null, ?string $persist_id_meta_key = null): ?string
    {
        $result = null;

        if (is_string($stored_url) && $stored_url !== '') {
            if (self::isLocalUploadUrl($stored_url)) {
                $result = self::resolveLocalStoredUrl($stored_url, $user_id, $persist_id_meta_key);
            } else {
                $result = $stored_url;
            }
        }

        return $result;
    }

    private static function resolveLocalStoredUrl(string $stored_url, ?int $user_id, ?string $persist_id_meta_key): ?string
    {
        $result = null;
        $resolved_id = self::getAttachmentIdFromUrl($stored_url);

        if ($resolved_id) {
            $avatar_url = self::resolveAttachmentIdToUrl((int) $resolved_id);
            if ($avatar_url !== null) {
                self::persistAttachmentIdForUser($user_id, $persist_id_meta_key, (int) $resolved_id);
                $result = $avatar_url;
            }
        }

        return $result;
    }

    private static function persistAttachmentIdForUser(?int $user_id, ?string $persist_id_meta_key, int $resolved_id): void
    {
        if ($user_id !== null && is_string($persist_id_meta_key) && $persist_id_meta_key !== '') {
            \update_user_meta($user_id, $persist_id_meta_key, $resolved_id);
        }
    }

    /**
     * Whether a URL points at the WordPress uploads directory (may be offloaded — not served locally).
     */
    private static function isLocalUploadUrl(string $url): bool
    {
        if ($url === '') {
            return false;
        }

        $upload_dir = \wp_upload_dir();
        $base_url = $upload_dir['baseurl'] ?? '';

        return ($base_url !== '' && strpos($url, $base_url) !== false)
            || strpos($url, '/wp-content/uploads/') !== false;
    }

    /**
     * True when the upload file still exists on disk (not fully offloaded / removed locally).
     */
    private static function localUploadFileExists(string $url): bool
    {
        if (!self::isLocalUploadUrl($url)) {
            return false;
        }

        $upload_dir = \wp_upload_dir();
        $can_resolve = empty($upload_dir['error'])
            && !empty($upload_dir['basedir'])
            && !empty($upload_dir['baseurl']);

        if (!$can_resolve) {
            return false;
        }

        $relative_path = self::resolveRelativeUploadPath($url, $upload_dir);

        return $relative_path !== '' && \file_exists($relative_path);
    }

    /**
     * @param array{basedir: string, baseurl: string} $upload_dir
     */
    private static function resolveRelativeUploadPath(string $url, array $upload_dir): string
    {
        $relative_path = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $url);
        if (is_string($relative_path) && $relative_path !== $url) {
            return $relative_path;
        }

        $path_from_url = parse_url($url, PHP_URL_PATH);
        if (!is_string($path_from_url) || $path_from_url === '') {
            return '';
        }

        return $upload_dir['basedir'] . $path_from_url;
    }

    private static function extractUploadFilename(string $url): string
    {
        $upload_dir = \wp_upload_dir();
        $base_url = $upload_dir['baseurl'] ?? '';
        $is_upload_url = ($base_url !== '' && strpos($url, $base_url) !== false)
            || strpos($url, '/wp-content/uploads/') !== false;

        if (!$is_upload_url) {
            return '';
        }

        $path = parse_url($url, PHP_URL_PATH);
        if (is_string($path) && $path !== '') {
            return basename($path);
        }

        return basename($url);
    }

    /**
     * @return int|false
     */
    private static function lookupAttachmentIdByFilename(string $filename)
    {
        global $wpdb;

        $found = $wpdb->get_var($wpdb->prepare(
            "SELECT post_id FROM {$wpdb->postmeta}
            WHERE meta_key = '_wp_attached_file'
            AND meta_value LIKE %s
            ORDER BY post_id DESC
            LIMIT 1",
            '%' . $wpdb->esc_like($filename) . '%'
        ));

        if (!$found) {
            $found = $wpdb->get_var($wpdb->prepare(
                "SELECT ID FROM {$wpdb->posts}
                WHERE post_type = 'attachment'
                AND guid LIKE %s
                ORDER BY ID DESC
                LIMIT 1",
                '%' . $wpdb->esc_like($filename) . '%'
            ));
        }

        return $found ? (int) $found : false;
    }
}
