<?php
/**
 * API key manager with AES-256 encryption
 *
 * @package SlideMind
 * @since 2.0.0
 */

namespace SlideMind\Core;

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

class ApiKeyManager {

    /**
     * Encryption method
     * @var string
     */
    private $cipher = 'AES-256-CBC';

    /**
     * IV length for AES-256-CBC
     * @var int
     */
    private $iv_length;

    /**
     * Constructor
     */
    public function __construct() {
        $this->iv_length = openssl_cipher_iv_length($this->cipher);
    }

    /**
     * Retrieves the encryption key
     *
     * @return string
     */
    private function get_encryption_key() {
        // Priority 1: Constant in wp-config.php
        if (defined('SLIDEMIND_ENCRYPTION_KEY')) {
            return SLIDEMIND_ENCRYPTION_KEY;
        }

        // Priority 2: Option in the database
        $key = get_option('slidemind_encryption_key');

        if ($key) {
            return base64_decode($key);
        }

        // Fallback: Generate a key (should not happen if activation was correct)
        $new_key = wp_generate_password(32, true, true);
        update_option('slidemind_encryption_key', base64_encode($new_key));

        return $new_key;
    }

    /**
     * Encrypts an API key
     *
     * @param string $api_key The plaintext API key
     * @return string The encrypted key (base64)
     */
    public function encrypt($api_key) {
        if (empty($api_key)) {
            return '';
        }

        $key = $this->get_encryption_key();

        // Generate a random IV
        $iv = openssl_random_pseudo_bytes($this->iv_length);

        // Encrypt the key
        $encrypted = openssl_encrypt(
            $api_key,
            $this->cipher,
            $key,
            OPENSSL_RAW_DATA,
            $iv
        );

        if ($encrypted === false) {
            return '';
        }

        // Return IV + encrypted data as base64
        return base64_encode($iv . $encrypted);
    }

    /**
     * Decrypts an API key
     *
     * @param string $encrypted_key The encrypted key (base64)
     * @return string The plaintext API key
     */
    public function decrypt($encrypted_key) {
        if (empty($encrypted_key)) {
            return '';
        }

        $key = $this->get_encryption_key();

        // Decode the base64
        $data = base64_decode($encrypted_key);

        if ($data === false) {
            return '';
        }

        // Extract the IV
        $iv = substr($data, 0, $this->iv_length);
        $encrypted = substr($data, $this->iv_length);

        // Decrypt
        $decrypted = openssl_decrypt(
            $encrypted,
            $this->cipher,
            $key,
            OPENSSL_RAW_DATA,
            $iv
        );

        return $decrypted !== false ? $decrypted : '';
    }

    /**
     * Validation patterns per provider
     * @var array
     */
    private static $key_patterns = [
        'openrouter' => '/^sk-[a-zA-Z0-9\-_]{20,}$/',
        'openai'     => '/^sk-[a-zA-Z0-9\-_]{20,}$/',
        'anthropic'  => '/^sk-ant-[a-zA-Z0-9\-_]{20,}$/',
        'mistral'    => '/^[a-zA-Z0-9]{20,}$/',
        'google'     => '/^AIzaSy[a-zA-Z0-9\-_]{30,}$/',
    ];

    /**
     * Checks if an API key is valid according to the provider format
     *
     * @param string $api_key  The API key to validate
     * @param string $provider The provider slug (openrouter, openai, anthropic, mistral)
     * @return bool
     */
    public function validate_key_format($api_key, $provider = 'openrouter') {
        $pattern = isset(self::$key_patterns[$provider]) ? self::$key_patterns[$provider] : '/^.{20,}$/';
        return preg_match($pattern, $api_key) === 1;
    }

    /**
     * Tests an API key according to the provider
     *
     * @param string $api_key  The API key to test
     * @param string $provider The provider slug
     * @return array ['valid' => bool, 'message' => string, 'credits' => float|null]
     */
    public function test_api_key($api_key, $provider = 'openrouter') {
        switch ($provider) {
            case 'openrouter':
                return $this->test_key_openrouter($api_key);

            case 'openai':
                return $this->test_key_openai($api_key);

            case 'anthropic':
                return $this->test_key_anthropic($api_key);

            case 'mistral':
                return $this->test_key_mistral($api_key);

            case 'google':
                return $this->test_key_google($api_key);

            default:
                return [
                    'valid'   => false,
                    'message' => __('Unknown provider', 'slidemind'),
                    'credits' => null,
                ];
        }
    }

    /**
     * Tests an OpenRouter key via /auth/key
     */
    private function test_key_openrouter($api_key) {
        $response = wp_remote_get('https://openrouter.ai/api/v1/auth/key', [
            'headers' => [
                'Authorization' => 'Bearer ' . $api_key,
            ],
            'timeout' => 10,
        ]);

        if (is_wp_error($response)) {
            return [
                'valid'   => false,
                /* translators: %s: connection error message */
                'message' => sprintf(__('Connection error: %s', 'slidemind'), $response->get_error_message()),
                'credits' => null,
            ];
        }

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

        if ($code === 200) {
            return [
                'valid'   => true,
                'message' => __('Valid API key', 'slidemind'),
                'credits' => isset($body['data']['limit']) ? $body['data']['limit'] : null,
            ];
        }

        return [
            'valid'   => false,
            'message' => isset($body['error']['message']) ? $body['error']['message'] : __('Invalid API key', 'slidemind'),
            'credits' => null,
        ];
    }

    /**
     * Tests an OpenAI key via /models
     */
    private function test_key_openai($api_key) {
        $response = wp_remote_get('https://api.openai.com/v1/models', [
            'headers' => [
                'Authorization' => 'Bearer ' . $api_key,
            ],
            'timeout' => 10,
        ]);

        return $this->parse_bearer_test_response($response);
    }

    /**
     * Tests a Mistral key via /models
     */
    private function test_key_mistral($api_key) {
        $response = wp_remote_get('https://api.mistral.ai/v1/models', [
            'headers' => [
                'Authorization' => 'Bearer ' . $api_key,
            ],
            'timeout' => 10,
        ]);

        return $this->parse_bearer_test_response($response);
    }

    /**
     * Tests a Google (Gemini) key via /models
     */
    private function test_key_google($api_key) {
        $response = wp_remote_get('https://generativelanguage.googleapis.com/v1beta/openai/models', [
            'headers' => [
                'Authorization' => 'Bearer ' . $api_key,
            ],
            'timeout' => 10,
        ]);

        return $this->parse_bearer_test_response($response);
    }

    /**
     * Tests an Anthropic key via a minimal request
     */
    private function test_key_anthropic($api_key) {
        $response = wp_remote_post('https://api.anthropic.com/v1/messages', [
            'headers' => [
                'x-api-key'         => $api_key,
                'anthropic-version'  => '2023-06-01',
                'Content-Type'       => 'application/json',
            ],
            'body'    => wp_json_encode([
                'model'      => 'claude-3-haiku-20240307',
                'max_tokens' => 1,
                'messages'   => [['role' => 'user', 'content' => 'hi']],
            ]),
            'timeout' => 10,
        ]);

        if (is_wp_error($response)) {
            return [
                'valid'   => false,
                /* translators: %s: connection error message */
                'message' => sprintf(__('Connection error: %s', 'slidemind'), $response->get_error_message()),
                'credits' => null,
            ];
        }

        $code = wp_remote_retrieve_response_code($response);

        // 200 = success, 401 = bad key, other codes with valid response = key works
        if ($code === 200) {
            return [
                'valid'   => true,
                'message' => __('Valid API key', 'slidemind'),
                'credits' => null,
            ];
        }

        if ($code === 401) {
            $body = json_decode(wp_remote_retrieve_body($response), true);
            return [
                'valid'   => false,
                'message' => isset($body['error']['message']) ? $body['error']['message'] : __('Invalid API key', 'slidemind'),
                'credits' => null,
            ];
        }

        // Other codes (429, 400, etc.) mean the key is valid but something else failed
        return [
            'valid'   => true,
            'message' => __('Valid API key', 'slidemind'),
            'credits' => null,
        ];
    }

    /**
     * Parses a standard Bearer test response (OpenAI, Mistral)
     */
    private function parse_bearer_test_response($response) {
        if (is_wp_error($response)) {
            return [
                'valid'   => false,
                /* translators: %s: connection error message */
                'message' => sprintf(__('Connection error: %s', 'slidemind'), $response->get_error_message()),
                'credits' => null,
            ];
        }

        $code = wp_remote_retrieve_response_code($response);

        if ($code === 200) {
            return [
                'valid'   => true,
                'message' => __('Valid API key', 'slidemind'),
                'credits' => null,
            ];
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);

        return [
            'valid'   => false,
            'message' => isset($body['error']['message']) ? $body['error']['message'] : __('Invalid API key', 'slidemind'),
            'credits' => null,
        ];
    }

    /**
     * Retrieves the global API key (decrypted) for a provider
     *
     * @param string $provider The provider slug
     * @return string
     */
    public function get_global_api_key($provider = 'openrouter') {
        $encrypted = get_option('slidemind_api_key_' . $provider, '');

        // Fallback to the legacy global key for OpenRouter (backward compatibility)
        if (empty($encrypted) && $provider === 'openrouter') {
            $encrypted = get_option('slidemind_global_api_key', '');
        }

        return $this->decrypt($encrypted);
    }

    /**
     * Checks if any LLM provider has a global API key configured.
     *
     * @return bool True if at least one provider has a key.
     */
    public function has_any_api_key(): bool {
        $slugs = LLMProxy::get_provider_slugs();
        foreach ( $slugs as $slug ) {
            if ( ! empty( $this->get_global_api_key( $slug ) ) ) {
                return true;
            }
        }
        return false;
    }

    /**
     * Saves the global API key (encrypted) for a provider
     *
     * @param string $api_key  The plaintext API key
     * @param string $provider The provider slug
     * @return bool
     */
    public function save_global_api_key($api_key, $provider = 'openrouter') {
        $encrypted = $this->encrypt($api_key);
        $option_name = 'slidemind_api_key_' . $provider;
        $result = update_option($option_name, $encrypted);

        // Also keep the legacy option for backward compatibility
        if ($provider === 'openrouter') {
            update_option('slidemind_global_api_key', $encrypted);
        }

        // Force WP object cache invalidation (Redis / Memcached)
        wp_cache_delete($option_name, 'options');
        wp_cache_delete('alloptions', 'options');

        // Invalidate the models list cache for this provider
        delete_transient('slidemind_' . $provider . '_models');

        return $result;
    }

    /**
     * Masks an API key for display
     *
     * @param string $api_key The API key
     * @return string The masked key (e.g. sk-or-****...****abc123)
     */
    public function mask_key($api_key) {
        if (strlen($api_key) < 12) {
            return '****';
        }

        $prefix = substr($api_key, 0, 6);
        $suffix = substr($api_key, -6);

        return $prefix . '****...' . $suffix;
    }
}
