<?php
/**
 * VmpFence Installation Manager
 *
 * Server-authoritative site identification system.
 * Calls POST /api/v1/public/sites/identify (no auth required) to obtain
 * a server-assigned UUID for this WordPress installation.
 *
 * Falls back to a locally-generated temp UUID if server is unreachable.
 * Retries via WP-Cron until a server UUID is obtained.
 *
 * @package    VMPFence
 * @subpackage Includes
 * @since      2.2.4
 */

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

class VmpFence_Installation {

    /**
     * WordPress option key for the site UUID
     * @var string
     */
    const UUID_OPTION = 'vmpfence_installation_uuid';

    /**
     * WordPress option key for the installation fingerprint hash
     * @var string
     */
    const HASH_OPTION = 'vmpfence_installation_hash';

    /**
     * WordPress option key for the installation created timestamp
     * @var string
     */
    const CREATED_OPTION = 'vmpfence_installation_created_at';

    /**
     * Get or create the installation UUID.
     *
     * First checks local storage. If not found or forced refresh,
     * calls the server to obtain a UUID. Falls back to temp UUID if offline.
     *
     * @since 2.2.4
     * @param bool $force_refresh Force re-identification with server
     * @return string|null The site UUID, or null on failure
     */
    public static function get_installation_uuid($force_refresh = false) {
        // Check local storage first
        if (!$force_refresh) {
            $existing = get_option(self::UUID_OPTION, '');
            if (!empty($existing) && strpos($existing, 'temp-') !== 0) {
                return $existing;
            }
        }

        // Try to identify with server
        $uuid = self::identify_with_server();
        if ($uuid) {
            update_option(self::UUID_OPTION, $uuid);
            update_option(self::CREATED_OPTION, current_time('mysql'));

            if (class_exists('VMPFence_Secure_Logger')) {
                VMPFence_Secure_Logger::log('info', 'Site UUID obtained from server: ' . substr($uuid, 0, 8) . '...');
            }
            return $uuid;
        }

        // Server unreachable — fall back to temp UUID
        return self::handle_offline_mode();
    }

    /**
     * Call the server's public site-identify endpoint.
     *
     * @since 2.2.4
     * @return string|null Server-assigned UUID, or null on failure
     */
    private static function identify_with_server() {
        // Build identification payload
        $site_data = array(
            'domain'            => self::normalize_domain(get_site_url()),
            'site_url'          => get_site_url(),
            'installation_hash' => self::get_installation_hash(),
            'plugin_version'    => defined('VMPFENCE_VERSION') ? VMPFENCE_VERSION : '2.2.4',
            'wp_version'        => get_bloginfo('version'),
            'php_version'       => PHP_VERSION,
        );

        // Include connection health diagnostics
        if (class_exists('VMPFence_API_Client')) {
            $site_data['connection_health'] = VMPFence_API_Client::get_connection_health_payload();
        }

        // Include API key if available
        if (class_exists('VMPFence_Config')) {
            $api_key = VMPFence_Config::get('central_api_key', '');
            if (!empty($api_key)) {
                $site_data['api_key'] = $api_key;
            }
        }

        // Get the API base URL
        $api_base_url = '';
        if (class_exists('VMPFence_Endpoint_Config')) {
            $api_base_url = VMPFence_Endpoint_Config::get_api_base_url();
        }

        if (empty($api_base_url)) {
            if (class_exists('VMPFence_Secure_Logger')) {
                VMPFence_Secure_Logger::log('warning', 'Site identification: No API base URL available');
            }
            return null;
        }

        $url = rtrim($api_base_url, '/') . '/public/sites/identify';

        // Make direct HTTP request (no auth needed — public endpoint)
        $response = wp_remote_post($url, array(
            'timeout'     => 15,
            'redirection' => 5,
            'httpversion' => '1.1',
            'sslverify'   => true,
            'headers'     => array(
                'Content-Type' => 'application/json',
                'Accept'       => 'application/json',
                'User-Agent'   => 'VMPFence/' . (defined('VMPFENCE_VERSION') ? VMPFENCE_VERSION : '2.2.4'),
            ),
            'body' => wp_json_encode($site_data),
        ));

        if (is_wp_error($response)) {
            if (class_exists('VMPFence_Secure_Logger')) {
                VMPFence_Secure_Logger::log('warning', 'Site identification failed: ' . $response->get_error_message());
            }
            return null;
        }

        $status_code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);

        if ($status_code >= 200 && $status_code < 300 && !empty($data['site_uuid'])) {
            return sanitize_text_field($data['site_uuid']);
        }

        if (class_exists('VMPFence_Secure_Logger')) {
            VMPFence_Secure_Logger::log('warning', 'Site identification unexpected response', array(
                'status' => $status_code,
                'body'   => substr($body, 0, 500),
            ));
        }

        return null;
    }

    /**
     * Handle offline mode — generate a temp UUID for local use.
     *
     * @since 2.2.4
     * @return string|null Temporary UUID prefixed with 'temp-'
     */
    private static function handle_offline_mode() {
        $existing = get_option(self::UUID_OPTION, '');

        // If we already have a temp UUID, keep it
        if (!empty($existing) && strpos($existing, 'temp-') === 0) {
            return $existing;
        }

        // Generate a temp UUID
        $temp_uuid = 'temp-' . self::generate_uuid();
        update_option(self::UUID_OPTION, $temp_uuid);

        if (class_exists('VMPFence_Secure_Logger')) {
            VMPFence_Secure_Logger::log('info', 'Generated temporary site UUID (server unreachable): ' . substr($temp_uuid, 0, 13) . '...');
        }

        return $temp_uuid;
    }

    /**
     * Generate a deterministic installation fingerprint hash.
     * Uses WordPress salt keys to create a unique but stable identifier.
     *
     * @since 2.2.4
     * @return string SHA-256 hash of the installation fingerprint
     */
    public static function get_installation_hash() {
        $cached = get_option(self::HASH_OPTION, '');
        if (!empty($cached)) {
            return $cached;
        }

        // Build fingerprint from stable WordPress config values
        $components = array(
            defined('AUTH_KEY') ? AUTH_KEY : '',
            defined('SECURE_AUTH_KEY') ? SECURE_AUTH_KEY : '',
            defined('LOGGED_IN_KEY') ? LOGGED_IN_KEY : '',
            get_site_url(),
            wp_normalize_path(ABSPATH),
        );

        $hash = hash('sha256', implode('|', $components));
        update_option(self::HASH_OPTION, $hash);

        return $hash;
    }

    /**
     * Normalize a domain URL to just the hostname.
     *
     * @since 2.2.4
     * @param string $url Full URL
     * @return string Normalized domain (e.g., 'example.com')
     */
    private static function normalize_domain($url) {
        $parsed = wp_parse_url($url);
        $host = isset($parsed['host']) ? $parsed['host'] : $url;

        // Remove www. prefix
        $host = preg_replace('/^www\./i', '', $host);

        return strtolower($host);
    }

    /**
     * Generate a random UUID v4.
     *
     * @since 2.2.4
     * @return string UUID v4 string
     */
    private static function generate_uuid() {
        if (function_exists('wp_generate_uuid4')) {
            return wp_generate_uuid4();
        }

        // Fallback for older WordPress versions
        $data = random_bytes(16);
        $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // Version 4
        $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // Variant RFC 4122

        return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
    }

    /**
     * Get complete site identity information for diagnostic purposes.
     *
     * @since 2.2.4
     * @return array Site identity details
     */
    public static function get_site_identity() {
        return array(
            'uuid'              => get_option(self::UUID_OPTION, ''),
            'hash'              => get_option(self::HASH_OPTION, ''),
            'created_at'        => get_option(self::CREATED_OPTION, ''),
            'domain'            => self::normalize_domain(get_site_url()),
            'is_temporary'      => strpos(get_option(self::UUID_OPTION, ''), 'temp-') === 0,
            'plugin_version'    => defined('VMPFENCE_VERSION') ? VMPFENCE_VERSION : 'unknown',
        );
    }
}
