<?php
namespace DS_Plugin;

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

class Updates {
    const CACHE_KEY = 'ds_updates_check_cache_v1';
    const CACHE_TTL = 900; // 15m
    const VERSION_SEEN_OPTION = 'ds_updates_version_seen_v1';
    const ADMIN_FORCE_CHECK_OPTION = 'ds_updates_admin_force_check_at_v1';
    const ADMIN_FORCE_CHECK_COOLDOWN = 300; // 5m

    public static function init() {
        add_filter('pre_set_site_transient_update_plugins', [__CLASS__, 'inject_update'], 20);
        add_filter('plugins_api', [__CLASS__, 'plugins_api'], 20, 3);
        add_filter('upgrader_pre_download', [__CLASS__, 'intercept_upgrader_download'], 20, 4);
        add_action('upgrader_process_complete', [__CLASS__, 'on_upgrader_complete'], 10, 2);
        add_action('update_option_' . Settings::OPTION_KEY, [__CLASS__, 'flush_cache'], 10, 2);
        add_action('init', [__CLASS__, 'maybe_flush_on_version_change']);
        add_action('admin_init', [__CLASS__, 'maybe_force_refresh_on_admin_update_screens']);
    }

    public static function flush_cache($old = null, $new = null) {
        delete_transient(self::CACHE_KEY);
        delete_site_transient('update_plugins');
    }

    public static function maybe_flush_on_version_change() {
        $current = self::current_version();
        $seen = (string) get_option(self::VERSION_SEEN_OPTION, '');
        if ($seen === $current) return;

        self::flush_cache();
        update_option(self::VERSION_SEEN_OPTION, $current, false);
    }

    public static function maybe_force_refresh_on_admin_update_screens() {
        if (!is_admin() || !current_user_can('update_plugins')) return;
        $pagenow = isset($GLOBALS['pagenow']) ? (string) $GLOBALS['pagenow'] : '';
        if ($pagenow !== 'plugins.php' && $pagenow !== 'update-core.php') return;

        $last = (int) get_option(self::ADMIN_FORCE_CHECK_OPTION, 0);
        if ($last > 0 && (time() - $last) < self::ADMIN_FORCE_CHECK_COOLDOWN) return;

        self::flush_cache();
        update_option(self::ADMIN_FORCE_CHECK_OPTION, time(), false);
    }

    public static function on_upgrader_complete($upgrader, $hook_extra) {
        if (!is_array($hook_extra)) return;
        $action = isset($hook_extra['action']) ? (string) $hook_extra['action'] : '';
        $type = isset($hook_extra['type']) ? (string) $hook_extra['type'] : '';
        if ($action !== 'update' || $type !== 'plugin') return;
        $plugins = isset($hook_extra['plugins']) && is_array($hook_extra['plugins']) ? $hook_extra['plugins'] : [];
        if (in_array(self::plugin_basename(), $plugins, true)) {
            self::flush_cache();
        }
    }

    public static function intercept_upgrader_download($reply, $package, $upgrader, $hook_extra) {
        if (false !== $reply) return $reply;
        $is_our_plugin = self::is_our_plugin_upgrade($hook_extra);
        $is_our_package = self::is_our_update_package_url($package);
        if (!$is_our_plugin && !$is_our_package) return $reply;

        $package = trim((string) $package);
        if ($package === '') {
            return new \WP_Error('ds_update_no_package', __('Update package not available.', 'ds'));
        }

        $safe_package = self::sanitize_download_url_for_display($package);
        if (is_object($upgrader) && isset($upgrader->skin) && is_object($upgrader->skin) && method_exists($upgrader->skin, 'feedback')) {
            $upgrader->skin->feedback('downloading_package', $safe_package);
        }

        if (!function_exists('download_url')) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }

        $download_file = download_url($package, 300, false);
        if (is_wp_error($download_file)) {
            return $download_file;
        }
        return $download_file;
    }

    public static function inject_update($transient) {
        if (!is_object($transient)) return $transient;
        $plugin = self::plugin_basename();
        if (empty($transient->checked) || !is_array($transient->checked) || !isset($transient->checked[$plugin])) {
            return $transient;
        }

        $res = self::request_remote('updates/check');
        if (!is_array($res)) return $transient;

        $new_version = isset($res['new_version']) ? trim((string) $res['new_version']) : '';
        if ($new_version === '' || !preg_match('/^\d+(\.\d+){0,3}([.-][A-Za-z0-9]+)?$/', $new_version)) {
            return $transient;
        }

        $current = self::current_version();
        $requires = isset($res['requires']) ? sanitize_text_field((string) $res['requires']) : '';
        $tested = isset($res['tested']) ? sanitize_text_field((string) $res['tested']) : '';
        $requires_php = isset($res['requires_php']) ? sanitize_text_field((string) $res['requires_php']) : '';
        $homepage = isset($res['homepage']) ? esc_url_raw((string) $res['homepage']) : '';
        $download = isset($res['download_url']) ? esc_url_raw((string) $res['download_url']) : '';

        $entry = (object) [
            'id' => self::plugin_slug(),
            'slug' => self::plugin_slug(),
            'plugin' => $plugin,
            'new_version' => $new_version,
            'url' => $homepage !== '' ? $homepage : 'https://lorandit.com',
            'package' => $download,
            'requires' => $requires,
            'tested' => $tested,
            'requires_php' => $requires_php,
        ];

        if (version_compare($new_version, $current, '>') && $download !== '') {
            if (!isset($transient->response) || !is_array($transient->response)) $transient->response = [];
            $transient->response[$plugin] = $entry;
            // Clear stale no_update entry if plugin just became updatable.
            if (isset($transient->no_update) && is_array($transient->no_update) && isset($transient->no_update[$plugin])) {
                unset($transient->no_update[$plugin]);
            }
        } else {
            if (!isset($transient->no_update) || !is_array($transient->no_update)) $transient->no_update = [];
            $transient->no_update[$plugin] = $entry;
            // Critical: clear stale response entry when offered version is not newer.
            if (isset($transient->response) && is_array($transient->response) && isset($transient->response[$plugin])) {
                unset($transient->response[$plugin]);
            }
        }

        return $transient;
    }

    public static function plugins_api($result, $action, $args) {
        if ($action !== 'plugin_information') return $result;
        $slug = isset($args->slug) ? (string) $args->slug : '';
        if ($slug !== self::plugin_slug()) return $result;

        $res = self::request_remote('updates/info');
        if (!is_array($res)) return $result;

        $sections = isset($res['sections']) && is_array($res['sections']) ? $res['sections'] : [];
        $description = isset($sections['description']) ? (string) $sections['description'] : __('Design Studio editor plugin.', 'ds');
        $changelog = isset($sections['changelog']) ? (string) $sections['changelog'] : '';

        return (object) [
            'name' => isset($res['name']) && $res['name'] !== '' ? sanitize_text_field((string) $res['name']) : 'Lorandit Design Studio Pro',
            'slug' => self::plugin_slug(),
            'version' => isset($res['new_version']) && $res['new_version'] !== '' ? sanitize_text_field((string) $res['new_version']) : self::current_version(),
            'author' => isset($res['author']) && $res['author'] !== '' ? (string) $res['author'] : '<a href="https://lorandit.com" target="_blank" rel="noopener noreferrer">Lorandit</a>',
            'homepage' => isset($res['homepage']) && $res['homepage'] !== '' ? esc_url_raw((string) $res['homepage']) : 'https://lorandit.com',
            'requires' => isset($res['requires']) ? sanitize_text_field((string) $res['requires']) : '',
            'tested' => isset($res['tested']) ? sanitize_text_field((string) $res['tested']) : '',
            'requires_php' => isset($res['requires_php']) ? sanitize_text_field((string) $res['requires_php']) : '',
            'download_link' => isset($res['download_url']) ? esc_url_raw((string) $res['download_url']) : '',
            'sections' => [
                'description' => wp_kses_post($description),
                'changelog' => wp_kses_post($changelog),
            ],
        ];
    }

    private static function request_remote($path) {
        $cached = get_transient(self::CACHE_KEY);
        $cache_key = md5((string) $path . '|' . self::current_version());
        if (is_array($cached) && isset($cached[$cache_key]) && is_array($cached[$cache_key])) {
            return $cached[$cache_key];
        }

        $creds = Protection::ensure_site_credentials(Settings::get());
        if (is_wp_error($creds) || !is_array($creds)) return null;
        $api_url = isset($creds['api_url']) ? trim((string) $creds['api_url']) : '';
        $api_token = isset($creds['api_token']) ? trim((string) $creds['api_token']) : '';
        $license_key = isset($creds['license_key']) ? trim((string) $creds['license_key']) : '';
        if ($api_url === '' || $api_token === '' || $license_key === '') return null;

        $endpoint = self::endpoint_url($api_url, $path);
        if ($endpoint === '') return null;

        $payload = [
            'slug' => self::plugin_slug(),
            'plugin' => self::plugin_basename(),
            'version' => self::current_version(),
            'license_key' => $license_key,
            'site_url' => home_url('/'),
            'domain' => self::site_domain(),
            'instance_id' => self::instance_id(),
            'wp_version' => get_bloginfo('version'),
            'php_version' => PHP_VERSION,
            'timestamp' => time(),
        ];

        $res = wp_remote_post($endpoint, [
            'timeout' => 8,
            'headers' => [
                'Content-Type' => 'application/json',
                'Authorization' => 'Bearer ' . $api_token,
            ],
            'body' => wp_json_encode($payload),
        ]);
        if (is_wp_error($res)) return null;

        $code = (int) wp_remote_retrieve_response_code($res);
        $body = wp_remote_retrieve_body($res);
        $json = json_decode((string) $body, true);
        if ($code < 200 || $code >= 300 || !is_array($json)) {
            return null;
        }

        $store = is_array($cached) ? $cached : [];
        $store[$cache_key] = $json;
        set_transient(self::CACHE_KEY, $store, self::CACHE_TTL);
        return $json;
    }

    private static function endpoint_url($base_url, $path) {
        $u = rtrim((string) $base_url, '/');
        if ($u === '') return '';
        $path = ltrim((string) $path, '/');
        if ($path === '') return '';

        // Normalize inputs like .../v1/license/check to .../v1 for update endpoints.
        if (preg_match('#/v1/(license|trial|capability)/#i', $u)) {
            $u = preg_replace('#/v1/.*$#i', '/v1', $u);
        }

        // If direct updates endpoint was entered, keep it and swap suffix as needed.
        if (preg_match('#/v1/updates/(check|info)$#i', $u)) {
            return preg_replace('#(check|info)$#i', basename($path), $u);
        }

        if (preg_match('#/v1$#i', $u)) {
            return $u . '/' . $path;
        }
        if (preg_match('#/v1/#i', $u)) {
            return $u . '/' . $path;
        }
        return $u . '/v1/' . $path;
    }

    private static function sanitize_download_url_for_display($url) {
        $url = trim((string) $url);
        if ($url === '') return '';

        $parts = wp_parse_url($url);
        if (!is_array($parts) || empty($parts['scheme']) || empty($parts['host'])) {
            $cut = explode('?', $url, 2);
            return esc_url_raw($cut[0]);
        }

        $scheme = (string) $parts['scheme'];
        $host = (string) $parts['host'];
        $user = isset($parts['user']) ? (string) $parts['user'] : '';
        $pass = isset($parts['pass']) ? (string) $parts['pass'] : '';
        $port = isset($parts['port']) ? ':' . (int) $parts['port'] : '';
        $path = isset($parts['path']) ? (string) $parts['path'] : '';

        $auth = '';
        if ($user !== '') {
            $auth = $user;
            if ($pass !== '') $auth .= ':' . $pass;
            $auth .= '@';
        }

        return esc_url_raw($scheme . '://' . $auth . $host . $port . $path);
    }

    private static function is_our_plugin_upgrade($hook_extra) {
        if (!is_array($hook_extra)) return false;

        $type = isset($hook_extra['type']) ? (string) $hook_extra['type'] : '';
        if ($type !== '' && $type !== 'plugin') return false;

        $plugin = self::plugin_basename();
        if (isset($hook_extra['plugin']) && (string) $hook_extra['plugin'] === $plugin) return true;
        if (isset($hook_extra['plugins']) && is_array($hook_extra['plugins']) && count($hook_extra['plugins']) === 1) {
            return in_array($plugin, $hook_extra['plugins'], true);
        }

        return false;
    }

    private static function is_our_update_package_url($url) {
        $url = trim((string) $url);
        if ($url === '') return false;

        $parts = wp_parse_url($url);
        if (!is_array($parts)) return false;

        $path = isset($parts['path']) ? (string) $parts['path'] : '';
        if ($path === '' || stripos($path, '/v1/updates/download') === false) {
            return false;
        }

        // Optional hardening by host check. Keep permissive fallback for custom control-plane domains.
        $host = isset($parts['host']) ? strtolower((string) $parts['host']) : '';
        $configured_host = self::configured_updates_host();
        if ($configured_host !== '' && $host !== '' && $host !== $configured_host && $host !== 'license.lorandit.com') {
            // Still allow if path clearly matches updates/download endpoint.
            return true;
        }

        return true;
    }

    private static function configured_updates_host() {
        $opts = Settings::get();
        $api_url = isset($opts['protection_license_api_url']) ? trim((string) $opts['protection_license_api_url']) : '';
        if ($api_url === '') return '';
        $host = wp_parse_url($api_url, PHP_URL_HOST);
        return is_string($host) ? strtolower($host) : '';
    }

    private static function plugin_basename() {
        return plugin_basename(DS_PLUGIN_FILE);
    }

    private static function plugin_slug() {
        return dirname(self::plugin_basename());
    }

    private static function current_version() {
        return defined('DS_PLUGIN_VERSION') ? (string) DS_PLUGIN_VERSION : '0.0.0';
    }

    private static function site_domain() {
        $host = wp_parse_url(home_url('/'), PHP_URL_HOST);
        return is_string($host) ? strtolower($host) : '';
    }

    private static function instance_id() {
        $k = 'ds_protection_instance_id';
        $v = (string) get_option($k, '');
        if ($v !== '') return sanitize_key($v);
        $raw = function_exists('wp_generate_uuid4') ? wp_generate_uuid4() : uniqid('ds', true);
        $id = sanitize_key(str_replace('-', '', (string) $raw));
        if ($id === '') $id = md5(home_url('/') . '|' . wp_salt('auth'));
        update_option($k, $id, false);
        return $id;
    }
}
