<?php
/**
 * Costudio_Crypto — lightweight symmetric encryption wrapper.
 *
 * Legacy symmetric encryption wrapper kept for internal reads that still use
 * the older CBC payload format.
 *
 * @package Contentosapp_Studio
 * @since   3.0.0
 */

defined( 'ABSPATH' ) || exit;

class Costudio_Crypto {

    const ALGO   = 'AES-256-CBC';
    const PREFIX = 'FOSe1:';

    // ── Public API ────────────────────────────────────────────────────────────

    /**
     * Encrypt a plain-text string.
     *
     * @param  string $plain
     * @return string  Prefixed base64-encoded JSON payload, or '' when empty.
     */
    public static function encrypt( string $plain ): string {
        if ( empty( $plain ) ) {
            return '';
        }

        $key = self::derive_key();
        $iv  = random_bytes( openssl_cipher_iv_length( self::ALGO ) );

        $ciphertext = openssl_encrypt( $plain, self::ALGO, $key, OPENSSL_RAW_DATA, $iv );

        if ( false === $ciphertext ) {
            return $plain; // openssl unavailable — store as-is
        }

        $payload = base64_encode(
            wp_json_encode( [
                'iv' => base64_encode( $iv ),
                'ct' => base64_encode( $ciphertext ),
            ] )
        );

        return self::PREFIX . $payload;
    }

    /**
     * Decrypt a value previously encrypted by encrypt().
     *
     * Returns the raw value unchanged when it was never encrypted
     * (backward compatibility for pre-1.1.0 plain-text passwords).
     *
     * @param  string $value
     * @return string
     */
    public static function decrypt( string $value ): string {
        if ( empty( $value ) ) {
            return '';
        }

        if ( strpos( $value, self::PREFIX ) !== 0 ) {
            return $value; // Not encrypted — return as-is
        }

        $json = base64_decode( substr( $value, strlen( self::PREFIX ) ) );
        $data = json_decode( $json, true );

        if ( ! isset( $data['iv'], $data['ct'] ) ) {
            return $value;
        }

        $iv    = base64_decode( $data['iv'] );

        foreach ( self::decryption_keys() as $key ) {
            $plain = openssl_decrypt(
                base64_decode( $data['ct'] ),
                self::ALGO,
                $key,
                OPENSSL_RAW_DATA,
                $iv
            );

            if ( false !== $plain ) {
                return $plain;
            }
        }

        return $value;
    }

    // ── Internal ──────────────────────────────────────────────────────────────

    /**
     * Derive a 32-byte key from WordPress AUTH_KEY.
     *
     * @return string 32 raw bytes
     */
    private static function derive_key(): string {
        $secret = function_exists( 'costudio_encryption_secret' )
            ? costudio_encryption_secret()
            : ( defined( 'AUTH_KEY' ) && '' !== AUTH_KEY ? AUTH_KEY : wp_salt( 'auth' ) );
        return hash( 'sha256', $secret . 'costudio-crypto-v1', true );
    }

    /**
     * Candidate keys for legacy reads. New writes never use the universal
     * fallback, but existing encrypted options from misconfigured installs
     * remain readable.
     *
     * @return string[]
     */
    private static function decryption_keys(): array {
        $keys = [ self::derive_key() ];

        if ( ! defined( 'AUTH_KEY' ) || '' === AUTH_KEY ) {
            $legacy = hash( 'sha256', 'costudio-fallback-auth-key-change-me' . 'costudio-crypto-v1', true );
            if ( $legacy !== $keys[0] ) {
                $keys[] = $legacy;
            }
        }

        return $keys;
    }
}
