<?php

namespace App\Services\ProductOwner\Drivers;

use App\Contracts\ProductOwnerClientContract;
use App\DTOs\ProductOwner\ActivationRequestData;
use App\DTOs\ProductOwner\ActivationResponseData;
use App\DTOs\ProductOwner\AdminUpdateCheckData;
use App\DTOs\ProductOwner\HeartbeatPayloadData;
use App\DTOs\ProductOwner\LicenseValidationResponseData;
use App\DTOs\ProductOwner\ModulePurchaseResponseData;
use App\DTOs\ProductOwner\WalletBalanceData;
use App\DTOs\ProductOwner\WalletLedgerData;
use App\Support\ProductOwnerUnavailable;
use App\Support\PlatformVersion;
use App\Services\ProductOwner\RequestSigner;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Client\PendingRequest;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;

class HttpProductOwnerDriver implements ProductOwnerClientContract
{
    public function __construct(
        private readonly RequestSigner $requestSigner,
    ) {}

    public function activate(ActivationRequestData $request): ActivationResponseData
    {
        $response = $this->request(fn () => $this->client()->post('/api/v1/activate', $request->toArray()));

        if ($response === null) {
            return $this->unavailableActivationResponse();
        }

        if ($response->failed()) {
            return new ActivationResponseData(
                success: false,
                message: $response->json('message') ?? 'Activation request failed.',
            );
        }

        return ActivationResponseData::fromArray($response->json());
    }

    public function validateLicense(string $licenseKey, string $installationId): LicenseValidationResponseData
    {
        $response = $this->request(fn () => $this->client()->post('/api/v1/license/validate', [
            'license_key' => $licenseKey,
            'installation_id' => $installationId,
        ]));

        if ($response === null) {
            return $this->unavailableLicenseResponse();
        }

        if ($response->failed()) {
            return new LicenseValidationResponseData(
                valid: false,
                status: 'invalid',
                message: $response->json('message') ?? 'License validation failed.',
            );
        }

        return LicenseValidationResponseData::fromArray($response->json());
    }

    public function sendHeartbeat(string $installationId, ?HeartbeatPayloadData $payload = null): bool
    {
        $body = array_merge(
            ['installation_id' => $installationId],
            $payload?->toArray() ?? [],
        );

        $response = $this->request(fn () => $this->client()->post('/api/v1/heartbeat', $body));

        return $response?->successful() ?? false;
    }

    public function fetchAdminIntegrityManifest(string $installationId, string $version): ?array
    {
        $response = $this->request(fn () => $this->client()->get('/api/v1/updates/admin/manifest', [
            'installation_id' => $installationId,
            'version' => $version,
        ]));

        if ($response === null || $response->failed()) {
            return null;
        }

        $manifest = $response->json('manifest');

        return is_array($manifest) ? $manifest : null;
    }

    public function listMarketplaceModules(): array
    {
        $response = $this->request(fn () => $this->client()->get('/api/v1/marketplace/modules'));

        if ($response === null || $response->failed()) {
            return [];
        }

        return $response->json('modules') ?? [];
    }

    public function getWalletBalance(string $installationId): ?WalletBalanceData
    {
        $response = $this->request(fn () => $this->client()->get('/api/v1/wallet/balance', [
            'installation_id' => $installationId,
        ]));

        if ($response === null || $response->failed()) {
            return null;
        }

        return WalletBalanceData::fromArray($response->json());
    }

    public function getWalletLedger(string $installationId): ?WalletLedgerData
    {
        $response = $this->request(fn () => $this->client()->get('/api/v1/wallet/ledger', [
            'installation_id' => $installationId,
        ]));

        if ($response === null || $response->failed()) {
            return null;
        }

        return WalletLedgerData::fromArray($response->json());
    }

    public function purchaseModule(string $installationId, string $moduleUuid): ModulePurchaseResponseData
    {
        $response = $this->request(fn () => $this->client()->post('/api/v1/marketplace/modules/'.$moduleUuid.'/purchase', [
            'installation_id' => $installationId,
        ]));

        if ($response === null) {
            return new ModulePurchaseResponseData(
                success: false,
                message: ProductOwnerUnavailable::MESSAGE,
            );
        }

        if ($response->failed()) {
            return new ModulePurchaseResponseData(
                success: false,
                message: $response->json('message') ?? 'Module purchase failed.',
            );
        }

        return ModulePurchaseResponseData::fromArray($response->json());
    }

    public function initializeModule(string $installationId, string $moduleUuid): bool
    {
        $response = $this->request(fn () => $this->client()->post('/api/v1/marketplace/modules/'.$moduleUuid.'/initialize', [
            'installation_id' => $installationId,
        ]));

        return $response?->successful() ?? false;
    }

    public function checkAdminUpdate(string $installationId, string $currentVersion): AdminUpdateCheckData
    {
        $current = PlatformVersion::current();

        $response = $this->request(fn () => $this->client()->get('/api/v1/updates/admin', [
            'installation_id' => $installationId,
            'current_version' => $currentVersion,
        ]));

        if ($response === null || $response->failed()) {
            return AdminUpdateCheckData::unavailable($current);
        }

        return AdminUpdateCheckData::fromRemote($response->json(), $current);
    }

    /**
     * @param  callable(): \Illuminate\Http\Client\Response  $callback
     */
    private function request(callable $callback): ?Response
    {
        try {
            return $callback();
        } catch (ConnectionException $exception) {
            return null;
        }
    }

    private function unavailableActivationResponse(): ActivationResponseData
    {
        return new ActivationResponseData(
            success: false,
            message: ProductOwnerUnavailable::MESSAGE,
        );
    }

    private function unavailableLicenseResponse(): LicenseValidationResponseData
    {
        return new LicenseValidationResponseData(
            valid: false,
            status: 'unavailable',
            message: ProductOwnerUnavailable::MESSAGE,
        );
    }

    private function client(): PendingRequest
    {
        $secret = config('product_owner.api_secret');

        return Http::baseUrl(rtrim(config('product_owner.base_url'), '/'))
            ->timeout(config('product_owner.timeout', 30))
            ->acceptJson()
            ->withHeaders($secret ? [
                'X-Signature' => $this->requestSigner->signPayload(
                    ['installation_id' => config('product_owner.installation_id')],
                    $secret
                ),
            ] : []);
    }
}
