<?php
/**
 * Build and normalize FleekDash update payloads for WordPress.
 *
 * @package FleekDash\Core
 */

namespace FleekDash\Core;

defined('ABSPATH') || exit;

class UpdatePayloadFactory
{
    private const PLUGIN_SLUG = 'fleekdash';

    private string $pluginFile;

    public function __construct(string $pluginFile)
    {
        $this->pluginFile = $pluginFile;
    }

    /**
     * @param array<string, mixed> $data
     */
    public function buildValidatedUpdate(array $data): ?\stdClass
    {
        $update = new \stdClass();
        $update->id = '0';
        $update->slug = self::PLUGIN_SLUG;
        $update->plugin = $this->pluginFile;
        $update->new_version = $data['version'] ?? '';
        $update->url = $data['url'] ?? 'https://fleekdash.com';
        $update->package = $data['download_url'] ?? '';
        if ($update->package === '' && !empty($update->new_version)) {
            $update->package = $this->getDownloadUrl((string) $update->new_version);
        }

        $update->tested = $data['tested'] ?? '';
        $update->requires = $data['requires'] ?? '';
        $update->requires_php = $data['requires_php'] ?? '';
        $update->release_channel = UpdatePreferences::resolveReleaseChannelFromPayload($data);
        $update->icons = $this->resolveUpdateIconsForPayload($data);
        $update->banners = !empty($data['banners']) && is_array($data['banners']) ? $data['banners'] : [];
        $update->upgrade_notice = $data['upgrade_notice'] ?? '';
        $update->compatibility = new \stdClass();
        $update->sections = $this->resolveUpdateSectionsForPayload($data);

        $packageOk = !empty($update->package)
            && $this->isAllowedPackageDownloadUrl((string) $update->package);
        $valid = !empty($update->new_version) && $packageOk;
        if (!$valid) {
            if (!empty($update->package) && !$packageOk) {
                fleekdash_log(
                    'UpdateChecker: Rejected package URL (not on allowlist): ' . $update->package,
                    'error',
                );
            }

            return null;
        }

        return $update;
    }

    public function ensureIcons(\stdClass $update): \stdClass
    {
        if (empty($update->icons) || !is_array($update->icons)) {
            $update->icons = $this->getPluginIcons();
        }

        return $update;
    }

    public function buildPluginInfo(\stdClass $update): \stdClass
    {
        $pluginInfo = new \stdClass();
        $pluginInfo->name = 'FleekDash V2';
        $pluginInfo->slug = self::PLUGIN_SLUG;
        $pluginInfo->version = $update->new_version;
        $pluginInfo->author = '<a href="https://fleekdash.com">FleekDash Team</a>';
        $pluginInfo->homepage = 'https://fleekdash.com';
        $pluginInfo->requires = $update->requires;
        $pluginInfo->tested = $update->tested;
        $pluginInfo->requires_php = $update->requires_php;
        $pluginInfo->download_link = $update->package;
        $pluginInfo->sections = $update->sections;
        $pluginInfo->banners = $update->banners;
        $pluginInfo->last_updated = date('Y-m-d H:i:s');

        return $pluginInfo;
    }

    /**
     * @param array<string, mixed> $data
     * @return array<string, mixed>
     */
    private function resolveUpdateIconsForPayload(array $data): array
    {
        if (!empty($data['icons']) && is_array($data['icons'])) {
            return $data['icons'];
        }

        return $this->getPluginIcons();
    }

    /**
     * @param array<string, mixed> $data
     * @return array<string, mixed>
     */
    private function resolveUpdateSectionsForPayload(array $data): array
    {
        if (!empty($data['sections']) && is_array($data['sections'])) {
            return $data['sections'];
        }

        return [
            'description' => '',
            'installation' => '',
            'changelog' => '',
        ];
    }

    /**
     * @return list<string>
     */
    private function getAllowedPackageHosts(): array
    {
        $hosts = ['storage.ltdhunt.com'];

        if (defined('FLEEKDASH_R2_BASE_URL')) {
            $r2 = \wp_parse_url((string) constant('FLEEKDASH_R2_BASE_URL'));
            if (!empty($r2['host'])) {
                $hosts[] = strtolower((string) $r2['host']);
            }
        }

        if (defined('FLEEKDASH_SUPABASE_URL')) {
            $supabase = \wp_parse_url((string) constant('FLEEKDASH_SUPABASE_URL'));
            if (!empty($supabase['host'])) {
                $hosts[] = strtolower((string) $supabase['host']);
            }
        }

        $hosts = array_values(array_unique(array_filter($hosts)));

        /**
         * Extra hostnames allowed for FleekDash update package URLs (e.g. staging CDN).
         *
         * @param list<string> $hosts Lowercase hostnames.
         */
        $filtered = \apply_filters('fleekdash_update_package_allowed_hosts', $hosts);

        return is_array($filtered) ? array_values(array_unique(array_map('strtolower', $filtered))) : $hosts;
    }

    private function isAllowedPackageDownloadUrl(string $url): bool
    {
        $parsed = \wp_parse_url($url);
        $schemeOk = is_array($parsed)
            && isset($parsed['scheme'])
            && $parsed['scheme'] === 'https'
            && empty($parsed['user'])
            && empty($parsed['pass'])
            && !empty($parsed['host']);
        $path = is_array($parsed) && isset($parsed['path']) ? (string) $parsed['path'] : '';
        $pathOk = $path !== '' && preg_match('/\.zip$/i', $path) === 1;
        $host = is_array($parsed) && isset($parsed['host']) ? strtolower((string) $parsed['host']) : '';

        return $schemeOk && $pathOk && $host !== '' && \in_array($host, $this->getAllowedPackageHosts(), true);
    }

    private function getDownloadUrl(string $version): string
    {
        $baseUrl = 'https://storage.ltdhunt.com';
        if (defined('FLEEKDASH_R2_BASE_URL')) {
            $baseUrl = (string) constant('FLEEKDASH_R2_BASE_URL');
        }

        return $baseUrl . '/fleekdash/releases/fleekdash-' . $version . '.zip';
    }

    /**
     * @return array<string, string>
     */
    private function getPluginIcons(): array
    {
        $icons = [];

        $pluginDir = defined('FLEEKDASH_PLUGIN_DIR')
            ? (string) FLEEKDASH_PLUGIN_DIR
            : plugin_dir_path($this->pluginFile);
        if (empty($pluginDir) || (!file_exists($pluginDir . 'fleekdash.php') && defined('ABSPATH'))) {
            $pluginSlug = dirname($this->pluginFile);
            $pluginDir = ABSPATH . 'wp-content/plugins/' . $pluginSlug . '/';
        }

        $pluginDir = rtrim(str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $pluginDir), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
        $pluginUrl = defined('FLEEKDASH_PLUGIN_URL')
            ? (string) FLEEKDASH_PLUGIN_URL
            : plugin_dir_url($this->pluginFile);
        $pluginUrl = rtrim($pluginUrl, '/') . '/';

        $icon128File = 'public/images/icon-128x128.png';
        $icon256File = 'public/images/icon-256x256.png';
        $icon128Path = $pluginDir . str_replace('/', DIRECTORY_SEPARATOR, $icon128File);
        $icon256Path = $pluginDir . str_replace('/', DIRECTORY_SEPARATOR, $icon256File);
        $icon128Url = $pluginUrl . $icon128File;
        $icon256Url = $pluginUrl . $icon256File;

        if (file_exists($icon128Path)) {
            $icons['1x'] = $icon128Url;
            $icons['128x128'] = $icon128Url;
        }

        if (file_exists($icon256Path)) {
            $icons['2x'] = $icon256Url;
            $icons['256x256'] = $icon256Url;
            $icons['default'] = $icon256Url;
        }

        return $icons;
    }
}
