<?php

namespace onespace\tools\activeApi\components\clients\facetec;

use onespace\tools\activeApi\components\clients\facetec\base\FaceTecClient;
use Yii;

/**
 * PBVerify FaceTec Client Extension
 *
 * Extends the base FacetecClient with PBVerify-specific functionality:
 * - Basic authentication setup
 * - Dynamic key retrieval and caching
 * - PBVerify-specific API endpoints
 *
 * @property string $apiUsername PBVerify API username
 * @property string $apiPassword PBVerify API password
 * @inheritdoc
 */
class PbVerifyClient extends FaceTecClient {
    public $apiUsername;
    public $apiPassword;
    public $baseHeaders;

    // API endpoints specific to PBVerify
    private const ENDPOINT_DEVICE_LICENSE = '/device-license-key';
    private const ENDPOINT_PRODUCTION_KEYS = '/production-keys';
    private const ENDPOINT_ENCRYPTION_KEY = '/public-face-map-encryption-key';
    private const ENDPOINT_MOBILE_KEYS = '/mobile-production-keys';

    private const CACHE_DURATION = 3600; // 1 hour
    private const CACHE_KEY_DEVICE = 'facetec.deviceKey';
    private const CACHE_KEY_PRODUCTION = 'facetec.productionKey';
    private const CACHE_KEY_ENCRYPTION = 'facetec.encryptionKey';
    private const CACHE_KEY_MOBILE = 'facetec.mobileProductionKey';

    private const DEFAULT_USER_AGENT_TEMPLATE = 'facetec|custom|server|id.1s.co.za|%s';

    public function __construct($config = []) {
        $this->apiUrl = 'https://facetec.veriid.com/v9'; // dev endpoint
        parent::__construct($config);
    }

    /**
     * Initialize the component after construction
     * Sets up authentication and retrieves dynamic keys
     */
    #[\Override]
    public function init(): void {
        parent::init();
        $this->setupAuthentication();
        $this->initializeDynamicKeys();
    }

    /**
     * Override getFaceMap to ensure user agent is always provided for PBVerify
     *
     * @param string $databaseRefId External database reference ID
     * @param string|null $userAgent Optional client user agent string
     * @return array|false API response data or false on failure
     */
    #[\Override]
    public function getFaceMap(string $databaseRefId, ?string $userAgent = null) {
        // PBVerify requires the X-User-Agent string to have content
        if ($userAgent === null) {
            $userAgent = $this->buildDefaultUserAgent();
        }

        return parent::getFaceMap($databaseRefId, $userAgent);
    }

    /**
     * Set up basic authentication headers
     */
    private function setupAuthentication(): void {
        if (empty($this->apiUsername) || empty($this->apiPassword)) {
            Yii::warning('PBVerify API credentials not provided', __METHOD__);
            return;
        }

        $credentials = 'Basic ' . base64_encode($this->apiUsername . ':' . $this->apiPassword);
        $this->baseHeaders = [
            'Authorization' => $credentials,
            'X-Device-Key' => $this->deviceKey,
        ];

        Yii::info('PBVerify authentication configured', __METHOD__);
    }

    /**
     * Initialize dynamic keys from PBVerify API
     */
    private function initializeDynamicKeys(): void {
        $this->deviceKey = $this->getDeviceKey();
        $this->productionKey = $this->getProductionKey();
        $this->faceScanPublicKey = $this->getEncryptionPublicKey();
        $this->mobileProductionKey = $this->getMobileProductionKey();
    }

    /**
     * Build default user agent string for PBVerify
     *
     * @return string Formatted user agent string
     */
    private function buildDefaultUserAgent(): string {
        return sprintf(self::DEFAULT_USER_AGENT_TEMPLATE, $this->deviceKey ?? 'unknown');
    }

    /**
     * Get device license key from PBVerify API (cached)
     *
     * @return string|false Device key or false on failure
     */
    private function getDeviceKey() {
        return $this->getCachedApiValue(
            self::CACHE_KEY_DEVICE,
            self::ENDPOINT_DEVICE_LICENSE,
            'DeviceLicenseKey'
        );
    }

    /**
     * Get production keys from PBVerify API (cached)
     *
     * @return array|false Production keys or false on failure
     */
    private function getProductionKey() {
        return $this->getCachedApiValue(
            self::CACHE_KEY_PRODUCTION,
            self::ENDPOINT_PRODUCTION_KEYS,
            'ProductionKeys'
        );
    }

    /**
     * Get encryption public key from PBVerify API (cached)
     *
     * @return string|false Encryption key or false on failure
     */
    private function getEncryptionPublicKey() {
        return $this->getCachedApiValue(
            self::CACHE_KEY_ENCRYPTION,
            self::ENDPOINT_ENCRYPTION_KEY,
            'PublicFaceMapEncryptionKey'
        );
    }

    /**
     * Get mobile production keys from PBVerify API (cached)
     *
     * @return array|false Mobile production keys or false on failure
     */
    private function getMobileProductionKey() {
        return $this->getCachedApiValue(
            self::CACHE_KEY_MOBILE,
            self::ENDPOINT_MOBILE_KEYS,
            'ProductionKeys'
        );
    }

    /**
     * Generic method to get and cache API values
     *
     * @param string $cacheKey Cache key for storing the value
     * @param string $endpoint API endpoint to call
     * @param string $responseKey Key in the response data to extract
     * @return mixed API value or false on failure
     */
    private function getCachedApiValue(string $cacheKey, string $endpoint, string $responseKey) {
        return Yii::$app->cache->getOrSet(
            $cacheKey,
            function () use ($endpoint, $responseKey) {
                return $this->fetchApiValue($endpoint, $responseKey);
            },
            self::CACHE_DURATION
        );
    }

    /**
     * Fetch a specific value from a PBVerify API endpoint
     *
     * @param string $endpoint API endpoint to call
     * @param string $responseKey Key in the response data to extract
     * @return mixed API value or false on failure
     */
    private function fetchApiValue(string $endpoint, string $responseKey) {

        $response = $this->makeGetRequest($endpoint, $this->baseHeaders);

        if ($response === false) {
            return false;
        }

        if (!isset($response[$responseKey])) {
            Yii::warning(
                "Expected key '{$responseKey}' not found in response from {$endpoint}",
                __METHOD__
            );
            return false;
        }

        return $response[$responseKey];
    }

    /**
     * Clear all cached PBVerify keys
     * Useful for forcing refresh of cached values
     */
    public function clearKeyCache(): void {

        $cacheKeys = [
            self::CACHE_KEY_DEVICE,
            self::CACHE_KEY_PRODUCTION,
            self::CACHE_KEY_ENCRYPTION,
            self::CACHE_KEY_MOBILE,
        ];

        foreach ($cacheKeys as $key) {
            Yii::$app->cache->delete($key);
        }

        Yii::info('PBVerify key cache cleared', __METHOD__);
    }

    /**
     * Check if all required keys are available
     * @return bool True if all keys are available
     */
    public function hasAllKeys(): bool {
        return !empty($this->deviceKey)
            && !empty($this->productionKey)
            && !empty($this->faceScanPublicKey)
            && !empty($this->mobileProductionKey);
    }
}
