<?php
/**
 * VMO Hosting Upgrade Manager
 *
 * Watches usage telemetry pushed from QuickLaunch-hosted tenant sites, decides
 * when their Vultr instance should move up a tier, surfaces an upgrade
 * recommendation banner in the dashboard, and — on user approval — triggers
 * the Vultr resize and starts the recurring credit deduction at 1.5x the
 * upstream Vultr cost (per VMO_MARKUP_HOSTING in vmo-pricing-policy).
 *
 * Trigger thresholds (any one keeps a tier "hot" for 24h, two triggers within
 * 7 days promote the recommendation to ACTION REQUIRED):
 *   - Disk usage     >= 60%
 *   - CPU 5-min avg  >= 50%  (>= 70% counts as TWO triggers in one sample)
 *   - Swap usage     >= 10%
 *   - 5xx error rate >= 2%   (rolling 1h window)
 *
 * Tier ladder (vmo_pricing_hosting_tiers()):
 *   starter ($6 provider / bundled) → business ($9 / $13.50/mo billed)
 *     → pro ($18 / $27) → performance ($36 / $54) → high_perf ($72 / $108)
 *
 * Recurring deduction:
 *   On approval we set user_meta vmo_hosting_active_upgrade = {tier_key,
 *   approved_at, monthly_charge_usd}. The existing monthly billing cron
 *   (vmo_monthly_hosting_charge cron event we register here) converts the
 *   USD charge to tokens via vmo_dollars_to_tokens() and calls
 *   vmo_deduct_tokens_fifo() on the user — if balance is insufficient we
 *   degrade the recommendation to "billing failed; will retry" and notify.
 *
 * @package VMO_AI_Website_Builder
 * @since 1.1.0
 */

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

if (!class_exists('VMO_Hosting_Upgrade')) {

class VMO_Hosting_Upgrade {

    const RECOMMENDATION_META   = 'vmo_hosting_upgrade_recommendation';
    const ACTIVE_UPGRADE_META   = 'vmo_hosting_active_upgrade';
    const TRIGGER_HISTORY_META  = 'vmo_hosting_trigger_history';
    const CRON_MONTHLY_CHARGE   = 'vmo_monthly_hosting_charge';
    const CRON_NIGHTLY_EVAL     = 'vmo_nightly_hosting_eval';

    /** @var VMO_Hosting_Upgrade|null */
    private static $instance = null;

    public static function instance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct() {
        add_action('rest_api_init', array($this, 'register_routes'));
        add_action(self::CRON_MONTHLY_CHARGE, array($this, 'process_monthly_charges'));
        add_action(self::CRON_NIGHTLY_EVAL,   array($this, 'evaluate_all_tenants'));
        add_action('admin_notices', array($this, 'render_recommendation_banner'));

        // Schedule crons (idempotent)
        if (!wp_next_scheduled(self::CRON_MONTHLY_CHARGE)) {
            wp_schedule_event(time() + DAY_IN_SECONDS, 'daily', self::CRON_MONTHLY_CHARGE);
        }
        if (!wp_next_scheduled(self::CRON_NIGHTLY_EVAL)) {
            wp_schedule_event(time() + HOUR_IN_SECONDS, 'daily', self::CRON_NIGHTLY_EVAL);
        }
    }

    /* ============================================================
     *  REST ROUTES
     * ============================================================ */

    public function register_routes() {
        register_rest_route('vmo-builder/v1', '/hosting/usage-report', array(
            'methods'             => 'POST',
            'callback'            => array($this, 'rest_receive_usage_report'),
            'permission_callback' => '__return_true', // X-VMO-License authenticated inside
        ));
        register_rest_route('vmo-builder/v1', '/hosting/recommendation', array(
            'methods'             => 'GET',
            'callback'            => array($this, 'rest_get_recommendation'),
            'permission_callback' => function () { return is_user_logged_in(); },
        ));
        register_rest_route('vmo-builder/v1', '/hosting/approve-upgrade', array(
            'methods'             => 'POST',
            'callback'            => array($this, 'rest_approve_upgrade'),
            'permission_callback' => function () { return is_user_logged_in(); },
        ));
        register_rest_route('vmo-builder/v1', '/hosting/dismiss-recommendation', array(
            'methods'             => 'POST',
            'callback'            => array($this, 'rest_dismiss_recommendation'),
            'permission_callback' => function () { return is_user_logged_in(); },
        ));
    }

    /**
     * Tenant-side cron POSTs sampled metrics here every 15 minutes.
     * Body: { disk_pct, cpu_pct, swap_pct, error_5xx_pct, sample_at }
     */
    public function rest_receive_usage_report($request) {
        $license_key = sanitize_text_field($request->get_header('X-VMO-License'));
        if (empty($license_key)) {
            return new WP_REST_Response(array('success' => false, 'message' => 'Missing X-VMO-License header.'), 401);
        }

        global $wpdb;
        $license = $wpdb->get_row($wpdb->prepare(
            "SELECT id, user_id, status FROM {$wpdb->prefix}vmo_licenses WHERE license_key = %s AND status = 'active'",
            $license_key
        ), ARRAY_A);
        if (!$license) {
            return new WP_REST_Response(array('success' => false, 'message' => 'Invalid or inactive license.'), 401);
        }

        $user_id = (int) $license['user_id'];
        $sample = array(
            'disk_pct'      => max(0.0, min(100.0, (float) $request->get_param('disk_pct'))),
            'cpu_pct'       => max(0.0, min(100.0, (float) $request->get_param('cpu_pct'))),
            'swap_pct'      => max(0.0, min(100.0, (float) $request->get_param('swap_pct'))),
            'error_5xx_pct' => max(0.0, min(100.0, (float) $request->get_param('error_5xx_pct'))),
            'sample_at'     => time(),
        );

        $this->record_sample($user_id, $sample);
        $rec = $this->evaluate_user($user_id);

        return new WP_REST_Response(array(
            'success'        => true,
            'recommendation' => $rec,
        ));
    }

    public function rest_get_recommendation($request) {
        $user_id = get_current_user_id();
        $rec = get_user_meta($user_id, self::RECOMMENDATION_META, true);
        $active = get_user_meta($user_id, self::ACTIVE_UPGRADE_META, true);
        return new WP_REST_Response(array(
            'recommendation' => is_array($rec) ? $rec : null,
            'active_upgrade' => is_array($active) ? $active : null,
        ));
    }

    public function rest_approve_upgrade($request) {
        $user_id = get_current_user_id();
        $rec = get_user_meta($user_id, self::RECOMMENDATION_META, true);
        if (!is_array($rec) || empty($rec['target_tier'])) {
            return new WP_REST_Response(array('success' => false, 'message' => 'No pending upgrade recommendation.'), 404);
        }

        $tier_key = sanitize_text_field($rec['target_tier']);
        $tiers = function_exists('vmo_pricing_hosting_tiers') ? vmo_pricing_hosting_tiers() : array();
        if (empty($tiers[$tier_key])) {
            return new WP_REST_Response(array('success' => false, 'message' => 'Unknown hosting tier.'), 400);
        }
        $tier = $tiers[$tier_key];

        // Preflight: user must have enough credits for at least the first month's charge
        $monthly_usd    = (float) $tier['user_price'];
        $monthly_tokens = function_exists('vmo_dollars_to_tokens') ? vmo_dollars_to_tokens($monthly_usd) : (int) ceil($monthly_usd / 0.000667);
        $balance        = (int) get_user_meta($user_id, 'vmo_token_balance', true);
        if ($balance < $monthly_tokens) {
            return new WP_REST_Response(array(
                'success'           => false,
                'message'           => 'Insufficient VMO Credits for the first month of the upgraded hosting tier. Please top up and try again.',
                'required_tokens'   => $monthly_tokens,
                'available_tokens'  => $balance,
                'monthly_usd'       => $monthly_usd,
            ), 402);
        }

        // Trigger Vultr resize
        $resize = $this->trigger_vultr_resize($user_id, $tier_key, $tier);
        if (is_wp_error($resize)) {
            return new WP_REST_Response(array(
                'success' => false,
                'message' => 'Hosting provider resize failed: ' . $resize->get_error_message(),
            ), 502);
        }

        // Charge the first month immediately
        if (function_exists('vmo_deduct_tokens_fifo')) {
            vmo_deduct_tokens_fifo($user_id, $monthly_tokens, 'hosting_upgrade_first_month_' . $tier_key);
        }

        // Activate recurring deduction
        update_user_meta($user_id, self::ACTIVE_UPGRADE_META, array(
            'tier_key'           => $tier_key,
            'monthly_usd'        => $monthly_usd,
            'monthly_tokens'     => $monthly_tokens,
            'provider_cost_usd'  => (float) $tier['provider_cost'],
            'approved_at'        => time(),
            'last_charged_at'    => time(),
            'next_charge_due_at' => strtotime('+30 days'),
            'vultr_resize'       => $resize,
        ));
        delete_user_meta($user_id, self::RECOMMENDATION_META);

        return new WP_REST_Response(array(
            'success'        => true,
            'message'        => 'Hosting upgraded to ' . $tier['label'] . '. First month charged.',
            'tier'           => $tier,
            'tokens_charged' => $monthly_tokens,
        ));
    }

    public function rest_dismiss_recommendation($request) {
        $user_id = get_current_user_id();
        $rec = get_user_meta($user_id, self::RECOMMENDATION_META, true);
        if (is_array($rec)) {
            $rec['dismissed_at'] = time();
            update_user_meta($user_id, self::RECOMMENDATION_META, $rec);
        }
        return new WP_REST_Response(array('success' => true));
    }

    /* ============================================================
     *  EVALUATION
     * ============================================================ */

    /** Append a sample to the rolling 7-day trigger history. */
    private function record_sample($user_id, array $sample) {
        $history = get_user_meta($user_id, self::TRIGGER_HISTORY_META, true);
        if (!is_array($history)) $history = array();
        $cutoff = time() - (7 * DAY_IN_SECONDS);

        $triggers_in_sample = 0;
        if ($sample['disk_pct']      >= 60) $triggers_in_sample++;
        if ($sample['cpu_pct']       >= 70) $triggers_in_sample += 2;
        elseif ($sample['cpu_pct']   >= 50) $triggers_in_sample++;
        if ($sample['swap_pct']      >= 10) $triggers_in_sample++;
        if ($sample['error_5xx_pct'] >= 2)  $triggers_in_sample++;

        if ($triggers_in_sample > 0) {
            $history[] = array(
                'at'       => $sample['sample_at'],
                'triggers' => $triggers_in_sample,
                'sample'   => $sample,
            );
        }
        // prune old
        $history = array_values(array_filter($history, function ($h) use ($cutoff) {
            return isset($h['at']) && (int) $h['at'] >= $cutoff;
        }));
        update_user_meta($user_id, self::TRIGGER_HISTORY_META, $history);
    }

    /** Compute total triggers in last 7 days, pick a target tier, persist recommendation. */
    private function evaluate_user($user_id) {
        $history = get_user_meta($user_id, self::TRIGGER_HISTORY_META, true);
        if (!is_array($history)) $history = array();

        $total = 0;
        foreach ($history as $h) {
            $total += (int) ($h['triggers'] ?? 0);
        }

        if ($total < 2) {
            // No recommendation yet
            delete_user_meta($user_id, self::RECOMMENDATION_META);
            return null;
        }

        $current_tier = $this->get_current_tier_key($user_id);
        $target_tier  = $this->pick_next_tier($current_tier);
        if (!$target_tier || $target_tier === $current_tier) {
            return null;
        }

        $tiers = function_exists('vmo_pricing_hosting_tiers') ? vmo_pricing_hosting_tiers() : array();
        $tier = $tiers[$target_tier] ?? null;
        if (!$tier) return null;

        $severity = ($total >= 5) ? 'action_required' : 'recommended';
        $rec = array(
            'current_tier'      => $current_tier,
            'target_tier'       => $target_tier,
            'target_label'      => $tier['label'],
            'monthly_usd'       => (float) $tier['user_price'],
            'specs'             => $tier['specs'] ?? '',
            'severity'          => $severity,
            'trigger_total'     => $total,
            'first_seen_at'     => isset($history[0]['at']) ? (int) $history[0]['at'] : time(),
            'computed_at'       => time(),
            'reason'            => $this->summarize_reasons($history),
        );
        update_user_meta($user_id, self::RECOMMENDATION_META, $rec);
        return $rec;
    }

    private function summarize_reasons(array $history) {
        $maxes = array('disk_pct' => 0, 'cpu_pct' => 0, 'swap_pct' => 0, 'error_5xx_pct' => 0);
        foreach ($history as $h) {
            $s = $h['sample'] ?? array();
            foreach ($maxes as $k => $v) {
                if (isset($s[$k]) && $s[$k] > $v) $maxes[$k] = $s[$k];
            }
        }
        $parts = array();
        if ($maxes['disk_pct']      >= 60) $parts[] = sprintf('disk %d%%', (int) $maxes['disk_pct']);
        if ($maxes['cpu_pct']       >= 50) $parts[] = sprintf('CPU %d%%', (int) $maxes['cpu_pct']);
        if ($maxes['swap_pct']      >= 10) $parts[] = sprintf('swap %d%%', (int) $maxes['swap_pct']);
        if ($maxes['error_5xx_pct'] >= 2)  $parts[] = sprintf('5xx errors %.1f%%', $maxes['error_5xx_pct']);
        return $parts ? implode(', ', $parts) : 'sustained pressure';
    }

    private function get_current_tier_key($user_id) {
        $active = get_user_meta($user_id, self::ACTIVE_UPGRADE_META, true);
        if (is_array($active) && !empty($active['tier_key'])) {
            return $active['tier_key'];
        }
        return function_exists('vmo_get_default_hosting_tier_key') ? vmo_get_default_hosting_tier_key() : 'starter';
    }

    private function pick_next_tier($current_tier) {
        $ladder = array('starter', 'business', 'pro', 'performance', 'high_perf');
        $idx = array_search($current_tier, $ladder, true);
        if ($idx === false || $idx >= count($ladder) - 1) return null;
        return $ladder[$idx + 1];
    }

    /* ============================================================
     *  CRONS
     * ============================================================ */

    public function evaluate_all_tenants() {
        global $wpdb;
        $rows = $wpdb->get_results("SELECT DISTINCT user_id FROM {$wpdb->prefix}vmo_licenses WHERE status = 'active'", ARRAY_A);
        if (!is_array($rows)) return;
        foreach ($rows as $row) {
            $this->evaluate_user((int) $row['user_id']);
        }
    }

    /** Daily cron: charge users whose next_charge_due_at has passed. */
    public function process_monthly_charges() {
        global $wpdb;
        $now = time();
        $user_ids = $wpdb->get_col($wpdb->prepare(
            "SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key = %s",
            self::ACTIVE_UPGRADE_META
        ));
        if (!is_array($user_ids)) return;

        foreach ($user_ids as $user_id) {
            $user_id = (int) $user_id;
            $active = get_user_meta($user_id, self::ACTIVE_UPGRADE_META, true);
            if (!is_array($active) || empty($active['next_charge_due_at'])) continue;
            if ((int) $active['next_charge_due_at'] > $now) continue;

            $tokens = (int) ($active['monthly_tokens'] ?? 0);
            if ($tokens <= 0) continue;

            $balance = (int) get_user_meta($user_id, 'vmo_token_balance', true);
            if ($balance < $tokens) {
                $active['billing_failed_at']   = $now;
                $active['billing_failure_msg'] = 'Insufficient credits at renewal';
                update_user_meta($user_id, self::ACTIVE_UPGRADE_META, $active);
                error_log(sprintf('[VMO Hosting] Monthly hosting charge failed for user %d: balance %d < required %d', $user_id, $balance, $tokens));
                continue;
            }

            if (function_exists('vmo_deduct_tokens_fifo')) {
                vmo_deduct_tokens_fifo($user_id, $tokens, 'hosting_recurring_' . ($active['tier_key'] ?? 'unknown'));
            }
            $active['last_charged_at']    = $now;
            $active['next_charge_due_at'] = strtotime('+30 days', $now);
            unset($active['billing_failed_at'], $active['billing_failure_msg']);
            update_user_meta($user_id, self::ACTIVE_UPGRADE_META, $active);
        }
    }

    /* ============================================================
     *  VULTR INTEGRATION
     * ============================================================ */

    /**
     * Trigger Vultr instance resize for this user's QuickLaunch site.
     *
     * Looks up the user's vultr_instance_id (set by QuickLaunch provisioning)
     * and PATCHes the instance plan to the upgraded vc2-* size. Returns the
     * Vultr response on success or WP_Error.
     */
    private function trigger_vultr_resize($user_id, $tier_key, array $tier) {
        $instance_id = get_user_meta($user_id, 'vmo_vultr_instance_id', true);
        if (empty($instance_id)) {
            return new WP_Error('no_instance', 'No Vultr instance is linked to this account. This account is not on the hosted lane.');
        }

        $plan_map = array(
            'starter'     => 'vc2-1c-1gb',
            'business'    => 'vc2-1c-2gb',
            'pro'         => 'vc2-2c-4gb',
            'performance' => 'vc2-4c-8gb',
            'high_perf'   => 'vhf-8c-16gb',
        );
        $target_plan = $plan_map[$tier_key] ?? null;
        if (!$target_plan) {
            return new WP_Error('no_plan_map', 'Tier ' . $tier_key . ' has no Vultr plan mapping.');
        }

        $vultr_key = get_option('vmo_vultr_api_key', '');
        if (empty($vultr_key)) {
            return new WP_Error('no_vultr_key', 'Vultr API key is not configured on the master.');
        }

        $response = wp_remote_request('https://api.vultr.com/v2/instances/' . rawurlencode($instance_id), array(
            'method'    => 'PATCH',
            'timeout'   => 30,
            'sslverify' => true,
            'headers'   => array(
                'Authorization' => 'Bearer ' . $vultr_key,
                'Content-Type'  => 'application/json',
            ),
            'body'      => wp_json_encode(array('plan' => $target_plan)),
        ));

        if (is_wp_error($response)) {
            return $response;
        }
        $code = wp_remote_retrieve_response_code($response);
        $body = json_decode((string) wp_remote_retrieve_body($response), true);
        if ($code >= 400) {
            $msg = is_array($body) && !empty($body['error']) ? (string) $body['error'] : ('HTTP ' . $code);
            return new WP_Error('vultr_resize_failed', $msg, array('code' => $code, 'body' => $body));
        }

        return array(
            'instance_id' => $instance_id,
            'target_plan' => $target_plan,
            'http_code'   => $code,
            'requested_at' => time(),
        );
    }

    /* ============================================================
     *  ADMIN BANNER
     * ============================================================ */

    public function render_recommendation_banner() {
        if (!is_admin() || !current_user_can('read')) return;
        $user_id = get_current_user_id();
        if (!$user_id) return;

        $rec = get_user_meta($user_id, self::RECOMMENDATION_META, true);
        if (!is_array($rec) || empty($rec['target_tier'])) return;
        if (!empty($rec['dismissed_at']) && (time() - (int) $rec['dismissed_at']) < (3 * DAY_IN_SECONDS)) return;

        $severity = $rec['severity'] ?? 'recommended';
        $class    = ($severity === 'action_required') ? 'notice notice-error' : 'notice notice-warning';
        $monthly  = number_format((float) ($rec['monthly_usd'] ?? 0), 2);
        $label    = esc_html($rec['target_label'] ?? $rec['target_tier']);
        $reason   = esc_html($rec['reason'] ?? '');
        $specs    = esc_html($rec['specs']  ?? '');

        printf(
            '<div class="%s is-dismissible"><p><strong>VMO Hosting:</strong> Your site is running hot (%s). Upgrading to <strong>%s</strong>%s would cost <strong>$%s/month</strong> (billed in VMO Credits at the 1.5× hosting rate). <a href="#" class="button button-primary" id="vmo-hosting-approve-upgrade" data-tier="%s">Approve upgrade</a> <a href="#" id="vmo-hosting-dismiss-upgrade" style="margin-left:8px;">Remind me later</a></p></div>',
            esc_attr($class),
            $reason,
            $label,
            $specs !== '' ? ' (' . $specs . ')' : '',
            $monthly,
            esc_attr($rec['target_tier'])
        );

        $rest_url = esc_url_raw(rest_url('vmo-builder/v1/hosting/'));
        $nonce    = wp_create_nonce('wp_rest');
        ?>
        <script>
        (function(){
            var approveBtn = document.getElementById('vmo-hosting-approve-upgrade');
            var dismissBtn = document.getElementById('vmo-hosting-dismiss-upgrade');
            function post(path) {
                return fetch(<?php echo wp_json_encode($rest_url); ?> + path, {
                    method: 'POST',
                    credentials: 'same-origin',
                    headers: { 'X-WP-Nonce': <?php echo wp_json_encode($nonce); ?>, 'Content-Type': 'application/json' },
                    body: '{}'
                }).then(function(r){ return r.json(); });
            }
            if (approveBtn) approveBtn.addEventListener('click', function(e){
                e.preventDefault();
                approveBtn.textContent = 'Upgrading…';
                approveBtn.setAttribute('disabled','disabled');
                post('approve-upgrade').then(function(j){
                    if (j && j.success) { window.location.reload(); }
                    else { alert((j && j.message) || 'Upgrade failed.'); approveBtn.removeAttribute('disabled'); approveBtn.textContent = 'Approve upgrade'; }
                }).catch(function(){ alert('Network error.'); approveBtn.removeAttribute('disabled'); approveBtn.textContent = 'Approve upgrade'; });
            });
            if (dismissBtn) dismissBtn.addEventListener('click', function(e){
                e.preventDefault();
                post('dismiss-recommendation').then(function(){ window.location.reload(); });
            });
        })();
        </script>
        <?php
    }
}

VMO_Hosting_Upgrade::instance();

} // class_exists
