<?php

if (!defined('ABSPATH')) {
    exit;
}

final class BBC_Updates
{
    public static function boot()
    {
        add_filter('pre_set_site_transient_update_plugins', array(__CLASS__, 'check'));
        add_filter('upgrader_pre_download', array(__CLASS__, 'verify_download'), 10, 4);
    }

    public static function check($transient)
    {
        if (!is_object($transient)) return $transient;
        $identity = get_option(BBC_OPTION_IDENTITY, array());
        if (empty($identity['update_base_url']) || empty($identity['update_public_key'])) return $transient;
        $response = wp_remote_get($identity['update_base_url'] . '/manifest.json', array('timeout' => 15));
        if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) return $transient;
        $manifest = json_decode(wp_remote_retrieve_body($response), true);
        if (!self::valid_manifest($manifest, $identity['update_public_key'])) return $transient;
        if (version_compare($manifest['version'], BBC_VERSION, '<=')) return $transient;
        $plugin = plugin_basename(BBC_PLUGIN_FILE);
        $update = new stdClass();
        $update->id = 'bbc://connector';
        $update->slug = 'bbc';
        $update->plugin = $plugin;
        $update->new_version = $manifest['version'];
        $update->package = $manifest['package_url'];
        $transient->response[$plugin] = $update;
        set_transient('bbc_approved_update', $manifest, 1800);
        return $transient;
    }

    public static function verify_download($reply, $package, $upgrader, $hook_extra)
    {
        if (!isset($hook_extra['plugin']) || $hook_extra['plugin'] !== plugin_basename(BBC_PLUGIN_FILE)) return $reply;
        $manifest = get_transient('bbc_approved_update');
        if (!is_array($manifest) || $package !== $manifest['package_url']) return new WP_Error('bbc_update_unapproved', 'BBC update package was not approved.');
        $file = download_url($package, 300);
        if (is_wp_error($file)) return $file;
        if (!hash_equals(strtolower($manifest['sha256']), strtolower(hash_file('sha256', $file)))) {
            @unlink($file);
            return new WP_Error('bbc_update_hash', 'BBC update package hash did not match.');
        }
        return $file;
    }

    private static function valid_manifest($manifest, $public_key)
    {
        if (!is_array($manifest) || !isset($manifest['version'], $manifest['expires_at'], $manifest['package_url'], $manifest['sha256'], $manifest['signature'])) return false;
        if (intval($manifest['expires_at']) < time() || !preg_match('/^[a-f0-9]{64}$/i', $manifest['sha256'])) return false;
        if (strpos($manifest['package_url'], 'https://') !== 0) return false;
        $signature = BBC_Auth::base64url_decode($manifest['signature']);
        $key = BBC_Auth::base64url_decode($public_key);
        $message = $manifest['version'] . "\n" . intval($manifest['expires_at']) . "\n" . $manifest['package_url'] . "\n" . strtolower($manifest['sha256']);
        return $signature !== false && $key !== false && sodium_crypto_sign_verify_detached($signature, $message, $key);
    }
}

