<?php
/**
 * KeyBackupService - Console-side MasterKey backup (escrow).
 *
 * Sends the plugin's MasterKey to Console for encrypted storage and
 * retrieves it via email/OTP verification when the local key is lost.
 *
 */
declare(strict_types=1);

namespace SecurWP\Modules\Cloud\Service;

use SecurWP\Includes\MasterKey;
use SecurWP\Includes\SecurWPKeyManager;
use SecurWP\Modules\Cloud\SecureApi;

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

class KeyBackupService {

    /**
     * Send current MasterKey to Console for encrypted backup.
     *
     * Called after successful activation and after key rotation.
     * Non-fatal: logs warning on failure, never throws.
     */
    public static function send_backup(): void {
        try {
            $master = MasterKey::get();
        } catch ( \Throwable $e ) {
            securwp_log( '[SecurWP] Key backup: cannot read MasterKey - ' . $e->getMessage() );
            return;
        }

        $settings = CloudSettings::get_all();
        $token  = (string) ( $settings['site_token'] ?? '' );
        $secret = (string) ( $settings['site_secret'] ?? '' );
        $lk     = (string) ( $settings['license_key'] ?? '' );

        if ( $token === '' || $secret === '' ) {
            return; // No credentials - can't authenticate.
        }

        $api = new SecureApi( $lk, $token, $secret );
        $payload = [
            'key_backup' => base64_encode( $master ),
        ];

        // Include current Ed25519 public key so Console can update it
        // (key rotation changes MasterKey → derived Ed25519 keypair changes).
        $pub = SecurWPKeyManager::get_site_public_key_b64();
        if ( $pub !== '' ) {
            $payload['site_key_public'] = $pub;
        }

        $result = $api->post( 'services/connect/key-backup', $payload );

        if ( is_wp_error( $result ) ) {
            securwp_log( '[SecurWP] Key backup upload failed: ' . $result->get_error_message() );
            return;
        }

        \SecurWP\Includes\SettingsStore::set_field( \SecurWP\Includes\SettingsStore::COL_STATE, 'key_backup_available', '1' );
    }

    /**
     * Build the identification payload sent with unauthenticated key-backup
     * requests.  Sends every identifier the plugin has so Console can match
     * using the best available one (site_uuid → license_key fallback).
     *
     * @return array{site_url: string, site_uuid: string, license_key: string}
     */
    private static function build_identity_payload(): array {
        $raw = CloudSettings::get_raw();

        $site_uuid   = sanitize_text_field( (string) ( $raw['site_uuid'] ?? '' ) );
        $license_key = sanitize_text_field( (string) ( $raw['license_key'] ?? '' ) );
        if ( $license_key === '' ) {
            $license_key = sanitize_text_field( (string) \SecurWP\Includes\SettingsStore::get_field( \SecurWP\Includes\SettingsStore::COL_STATE, 'last_license_key', '' ) );
        }

        return [
            'site_url'    => home_url(),
            'site_uuid'   => $site_uuid,
            'license_key' => $license_key,
        ];
    }

    /**
     * Request OTP for key backup retrieval.
     *
     * Uses unauthenticated API call (no HMAC - key is lost).
     * Sends site_uuid + license_key for redundant site identification.
     *
     * @return array|\WP_Error Console response or error.
     */
    public static function request_restore_otp() {
        $identity = self::build_identity_payload();

        if ( $identity['site_uuid'] === '' && $identity['license_key'] === '' ) {
            return new \WP_Error(
                'no_site_identity',
                'No site identity found. This site must have been connected to SecurWP Console at least once.'
            );
        }

        $api = new SecureApi( '', '', '' );
        return $api->post( 'services/connect/key-backup/request-otp', $identity );
    }

    /**
     * Retrieve MasterKey backup from Console and restore it locally.
     *
     * @param string $email Site owner's email.
     * @param string $otp   OTP code received via email.
     * @return true|\WP_Error True on success or WP_Error on failure.
     */
    public static function retrieve_and_restore( string $email, string $otp ) {
        $identity = self::build_identity_payload();

        if ( $identity['site_uuid'] === '' && $identity['license_key'] === '' ) {
            return new \WP_Error( 'no_site_identity', 'No site identity found.' );
        }

        $api = new SecureApi( '', '', '' );
        $response = $api->post( 'services/connect/key-backup/retrieve', array_merge(
            $identity,
            [
                'email' => $email,
                'otp'   => $otp,
            ]
        ) );

        if ( is_wp_error( $response ) ) {
            return $response;
        }

        // Validate the returned key.
        $key_b64 = $response['key_backup'] ?? '';
        $raw_key = base64_decode( (string) $key_b64, true );
        if ( ! is_string( $raw_key ) || strlen( $raw_key ) !== 32 ) {
            return new \WP_Error( 'invalid_backup', 'Console returned an invalid key backup.' );
        }

        // Write the restored key to disk.
        $write_result = MasterKey::write_key_file( $raw_key );
        if ( is_wp_error( $write_result ) ) {
            return $write_result;
        }

        // Switch in-memory key so SecretManager uses the restored key immediately.
        MasterKey::clear_cache();
        MasterKey::set_override( $raw_key );

        // Save fresh credentials from Console (they re-issued site_token/site_secret).
        $settings = CloudSettings::get_all();
        if ( ! empty( $response['site_token'] ) && ! empty( $response['site_secret'] ) ) {
            $settings['site_token']  = (string) $response['site_token'];
            $settings['site_secret'] = (string) $response['site_secret'];
        }
        if ( ! empty( $response['license_key'] ) ) {
            $settings['license_key'] = (string) $response['license_key'];
        }
        $settings['license_status'] = (string) ( $response['license_status'] ?? $settings['license_status'] ?? 'inactive' );
        $settings['license_plan']   = (string) ( $response['plan'] ?? $settings['license_plan'] ?? 'free' );
        unset( $settings['site_token_enc'], $settings['site_secret_enc'] );
        CloudSettings::save( $settings );

        \SecurWP\Includes\SettingsStore::set_field( \SecurWP\Includes\SettingsStore::COL_STATE, 'key_backup_available', '1' );

        return true;
    }
}
