<?php

declare(strict_types=1);

namespace Numinix\SeekmodoWordPress\Sdk;

use GuzzleHttp\Psr7\HttpFactory;
use Numinix\SeekmodoSdk\AutoPromoter as SdkAutoPromoter;
use Numinix\SeekmodoSdk\BrowserToken as SdkBrowserToken;
use Numinix\SeekmodoSdk\CircuitBreaker as SdkCircuitBreaker;
use Numinix\SeekmodoSdk\Client as SdkClient;
use Numinix\SeekmodoSdk\Events\EventsQueue as SdkEventsQueue;
use Numinix\SeekmodoSdk\HmacSigner as SdkHmacSigner;
use Numinix\SeekmodoSdk\ModeFsm as SdkModeFsm;
use Numinix\SeekmodoSdk\Pairing as SdkPairing;
use Numinix\SeekmodoSdk\SignatureMismatchTracker as SdkSignatureMismatchTracker;
use Numinix\SeekmodoSdk\TenantSnapshot as SdkTenantSnapshot;
use Numinix\SeekmodoWordPress\Config\Options;
use Numinix\SeekmodoWordPress\Storage\WpObjectCache;
use Numinix\SeekmodoWordPress\Storage\WpOptionsEventQueueStore;
use Numinix\SeekmodoWordPress\Storage\WpTransientBreakerStore;

/**
 * Lazy-constructed singletons that wire the shared
 * `numinix/seekmodo-connector` SDK with WordPress-flavoured storage
 * and the connector's existing {@see Options} config readers.
 *
 * Usage from new code:
 *
 *   $client = \Numinix\SeekmodoWordPress\Sdk\Bootstrap::client();
 *   $results = $client->search(['q' => 'red shoes']);
 *
 * Existing call sites (`Numinix\SeekmodoWordPress\Gateway\Client::post()`,
 * etc.) keep working unchanged in v0.7.0 — they're the BC surface
 * we'll collapse-to-adapter in v0.8.x. See `MIGRATION.md` for the
 * collapse plan.
 *
 * All getters are idempotent — first call builds and caches the
 * instance, subsequent calls return the same one. Reset for tests
 * via {@see self::reset()}.
 */
final class Bootstrap
{
    private static ?SdkHmacSigner $signer = null;
    private static ?SdkClient $client = null;
    private static ?SdkCircuitBreaker $breaker = null;
    private static ?WpObjectCache $cache = null;
    private static ?SdkTenantSnapshot $snapshot = null;
    private static ?SdkModeFsm $modeFsm = null;
    private static ?SdkAutoPromoter $autoPromoter = null;
    private static ?SdkPairing $pairing = null;
    private static ?SdkBrowserToken $browserToken = null;
    private static ?SdkEventsQueue $eventsQueue = null;
    private static ?SdkSignatureMismatchTracker $sigTracker = null;

    private function __construct()
    {
    }

    public static function client(): SdkClient
    {
        if (self::$client === null) {
            $factory = new HttpFactory();
            self::$client = new SdkClient(
                self::signer(),
                new WpHttpClient(2.0),
                $factory,
                $factory,
                Options::gatewayUrl() ?: SdkClient::DEFAULT_GATEWAY_URL,
                self::breaker(),
                null,
                'seekmodo-wordpress-connector/' . self::pluginVersion(),
                \Numinix\SeekmodoWordPress\Gateway\DomainLock::currentHost(),
            );
        }
        return self::$client;
    }

    public static function signer(): SdkHmacSigner
    {
        if (self::$signer === null) {
            self::$signer = new SdkHmacSigner(Options::tenantId(), Options::sharedSecret());
        }
        return self::$signer;
    }

    public static function breaker(): SdkCircuitBreaker
    {
        if (self::$breaker === null) {
            self::$breaker = new SdkCircuitBreaker(new WpTransientBreakerStore());
        }
        return self::$breaker;
    }

    public static function cache(): WpObjectCache
    {
        if (self::$cache === null) {
            self::$cache = new WpObjectCache();
        }
        return self::$cache;
    }

    public static function snapshot(): SdkTenantSnapshot
    {
        if (self::$snapshot === null) {
            self::$snapshot = new SdkTenantSnapshot(self::client(), self::cache());
        }
        return self::$snapshot;
    }

    public static function modeFsm(): SdkModeFsm
    {
        if (self::$modeFsm === null) {
            self::$modeFsm = new SdkModeFsm(self::snapshot(), self::breaker());
        }
        return self::$modeFsm;
    }

    public static function autoPromoter(): SdkAutoPromoter
    {
        if (self::$autoPromoter === null) {
            self::$autoPromoter = new SdkAutoPromoter(self::snapshot(), self::breaker(), self::cache());
        }
        return self::$autoPromoter;
    }

    public static function pairing(): SdkPairing
    {
        if (self::$pairing === null) {
            $factory = new HttpFactory();
            self::$pairing = new SdkPairing(
                new WpHttpClient(5.0),
                $factory,
                self::cache(),
            );
        }
        return self::$pairing;
    }

    public static function browserToken(): SdkBrowserToken
    {
        if (self::$browserToken === null) {
            self::$browserToken = new SdkBrowserToken(self::client(), self::cache());
        }
        return self::$browserToken;
    }

    public static function eventsQueue(): SdkEventsQueue
    {
        if (self::$eventsQueue === null) {
            self::$eventsQueue = new SdkEventsQueue(self::client(), new WpOptionsEventQueueStore());
        }
        return self::$eventsQueue;
    }

    public static function signatureMismatchTracker(): SdkSignatureMismatchTracker
    {
        if (self::$sigTracker === null) {
            self::$sigTracker = new SdkSignatureMismatchTracker(self::cache());
        }
        return self::$sigTracker;
    }

    /**
     * Discard all cached instances. Used by tests and by the admin's
     * "Re-pair" flow so subsequent gateway calls pick up freshly
     * persisted tenant_id / shared_secret values.
     */
    public static function reset(): void
    {
        self::$signer = null;
        self::$client = null;
        self::$breaker = null;
        self::$cache = null;
        self::$snapshot = null;
        self::$modeFsm = null;
        self::$autoPromoter = null;
        self::$pairing = null;
        self::$browserToken = null;
        self::$eventsQueue = null;
        self::$sigTracker = null;
    }

    private static function pluginVersion(): string
    {
        if (defined('SEEKMODO_WP_PLUGIN_VERSION')) {
            return (string) constant('SEEKMODO_WP_PLUGIN_VERSION');
        }
        return 'dev';
    }
}
