<?php

declare(strict_types=1);

final class MyronModuleLicenseManagerService
{
    public function __construct(
        private readonly MyronModuleEntitlementRepository $repository,
        private readonly MyronLicenseAuditRepository $auditRepository,
        private readonly ?MyronLicenseActivationService $activationService = null,
        private readonly ?MyronRemoteModulePackageClient $packageClient = null,
    ) {
    }

    /**
     * @return array{modules:list<array<string,mixed>>, catalogEmpty:bool, serviceReachable:bool, stage:string}
     */
    public function listModules(): array
    {
        $modules = $this->repository->listModules();
        foreach ($modules as &$module) {
            $installedVersion = trim((string) ($module['installedVersion'] ?? ''));
            $latestVersion = '';
            if ($this->packageClient instanceof MyronRemoteModulePackageClient) {
                try {
                    $remote = $this->packageClient->inspectPackageForSku((string) ($module['skuId'] ?? ''));
                    if (($remote['status'] ?? '') === 'module_package_available') {
                        $latestVersion = trim((string) ($remote['latestVersion'] ?? ''));
                    }
                } catch (Throwable) {
                    $latestVersion = '';
                }
            }
            $updateAvailable = false;
            if ($installedVersion !== '' && $latestVersion !== '') {
                try {
                    $updateAvailable = (new MyronVersionComparator())->isNewer($latestVersion, $installedVersion);
                } catch (Throwable) {
                    $updateAvailable = false;
                }
            }
            $module['latestVersion'] = $latestVersion;
            $module['updateAvailable'] = $updateAvailable;
            if ($updateAvailable) {
                $module['packageStatus'] = 'update_available';
            }
        }
        unset($module);
        return [
            'modules' => $modules,
            'catalogEmpty' => $modules === [],
            'serviceReachable' => $this->activationService instanceof MyronLicenseActivationService,
            'stage' => $this->activationService instanceof MyronLicenseActivationService ? 'stage_a_module_bridge' : 'stage_a',
        ];
    }

    /**
     * @return array{entitlements:list<array<string,mixed>>, catalogEmpty:bool, serviceReachable:bool, stage:string}
     */
    public function getEntitlement(?string $moduleSlug = null): array
    {
        $entitlements = $this->repository->entitlements($moduleSlug);
        return [
            'entitlements' => $entitlements,
            'catalogEmpty' => $entitlements === [],
            'serviceReachable' => $this->activationService instanceof MyronLicenseActivationService,
            'stage' => $this->activationService instanceof MyronLicenseActivationService ? 'stage_a_module_bridge' : 'stage_a',
        ];
    }

    /**
     * @return array{modules:list<array<string,mixed>>, catalogEmpty:bool, serviceReachable:bool, stage:string, operatorMessage:string}
     */
    public function refreshEntitlements(string $actorUserId = ''): array
    {
        $state = $this->listModules();
        $state['operatorMessage'] = 'Module entitlement refresh will be available when the MyronCMS Module catalog is live.';
        return $state;
    }

    /**
     * @return array{outcome:string, moduleSlug:null|string, auditEventId:string, operatorMessage:string}
     */
    public function redeemKey(string $licenseKey, string $actorUserId = '', string $skuId = 'pro-funnels'): array
    {
        $key = trim($licenseKey);
        $module = $this->moduleDefinitionForSku($skuId);
        if ($key === '') {
            $this->auditRepository->append('module_redeem.rejected_invalid', 'denied', 'missing_license_key', [
                'environment' => $this->environment(),
                'keyPrefix' => '',
                'keyMask' => '',
                'moduleSlug' => $module['slug'],
                'skuId' => $module['skuId'],
                'catalogEmpty' => true,
                'stage' => 'stage_a',
            ], $actorUserId);
            throw new InvalidArgumentException('License key is required.');
        }

        if (!$this->looksLikeLicenseKey($key)) {
            $this->auditRepository->append('module_redeem.rejected_invalid', 'denied', 'invalid_license_key_shape', [
                'environment' => $this->environment(),
                'keyPrefix' => $this->keyPrefix($key),
                'keyMask' => $this->maskKey($key),
                'moduleSlug' => $module['slug'],
                'skuId' => $module['skuId'],
                'catalogEmpty' => true,
                'stage' => 'stage_a',
            ], $actorUserId);
            throw new InvalidArgumentException('That does not look like a Module license key.');
        }

        if (!$this->activationService instanceof MyronLicenseActivationService) {
            $this->auditRepository->append('module_redeem.stage_a_unavailable', 'denied', 'module_catalog_unavailable', [
                'environment' => $this->environment(),
                'keyPrefix' => $this->keyPrefix($key),
                'keyMask' => $this->maskKey($key),
                'moduleSlug' => $module['slug'],
                'skuId' => $module['skuId'],
                'catalogEmpty' => true,
                'stage' => 'stage_a',
            ], $actorUserId);

            return [
                'outcome' => 'rejected_service_unreachable',
                'moduleSlug' => null,
                'auditEventId' => '',
                'operatorMessage' => 'Module license redemption is not available until the MyronCMS Module catalog is live.',
            ];
        }

        try {
            $result = $this->activationService->activateFromInput([
                'license_key' => $key,
                'license_plan' => 'core',
                'license_environment' => $this->environment(),
                'api_version' => 'v2',
                'skus' => [$module['skuId']],
            ], $this->siteUrl());
            $snapshot = MyronEntitlementSnapshot::fromArray(is_array($result['payload'] ?? null) ? $result['payload'] : []);
            if ($snapshot->status !== MyronLicenseStatus::ACTIVE || !$this->snapshotHasSku($snapshot, $module['skuId'])) {
                $this->auditRepository->append('module_redeem.rejected', 'denied', 'sku_not_active', [
                    'environment' => $this->environment(),
                    'keyPrefix' => $this->keyPrefix($key),
                    'keyMask' => $this->maskKey($key),
                    'moduleSlug' => $module['slug'],
                    'skuId' => $module['skuId'],
                    'status' => $snapshot->status,
                    'stage' => 'stage_a_module_bridge',
                ], $actorUserId);

                return [
                    'outcome' => 'rejected_sku_not_active',
                    'moduleSlug' => null,
                    'auditEventId' => '',
                    'operatorMessage' => 'That license key did not activate the ' . $module['displayName'] . ' module on this site.',
                ];
            }

            $packageResult = $this->maybeApplyActiveModulePackages($snapshot, $actorUserId);
            $this->auditRepository->append('module_redeem.succeeded', 'ok', '', [
                'environment' => $snapshot->environment,
                'keyPrefix' => $this->keyPrefix($key),
                'keyMask' => $this->maskKey($key),
                'moduleSlug' => $module['slug'],
                'skuId' => $module['skuId'],
                'status' => $snapshot->status,
                'plan' => $snapshot->plan,
                'stage' => 'stage_a_module_bridge',
                'packageHandoff' => $packageResult,
            ], $actorUserId);

            return [
                'outcome' => 'activated',
                'moduleSlug' => $module['slug'],
                'auditEventId' => '',
                'operatorMessage' => $this->packageStatusMessage($module['displayName'], $packageResult),
            ];
        } catch (Throwable $exception) {
            $reason = $this->activationService->publicReasonCode($exception);
            $this->auditRepository->append('module_redeem.failed', 'failed', $reason, [
                'environment' => $this->environment(),
                'keyPrefix' => $this->keyPrefix($key),
                'keyMask' => $this->maskKey($key),
                'moduleSlug' => $module['slug'],
                'skuId' => $module['skuId'],
                'stage' => 'stage_a_module_bridge',
            ], $actorUserId);
            throw new RuntimeException($this->activationService->safeErrorMessage($reason));
        }
    }

    /**
     * @return array{outcome:string,moduleSlug:null|string,auditEventId:string,operatorMessage:string,packageHandoff:array<string,mixed>}
     */
    public function installPackageForSku(string $skuId, string $actorUserId = ''): array
    {
        $module = $this->moduleDefinitionForSku($skuId);
        $snapshot = $this->repository->currentSnapshotForRuntime();
        if (!$snapshot instanceof MyronEntitlementSnapshot || $snapshot->status !== MyronLicenseStatus::ACTIVE || !$this->snapshotHasSku($snapshot, $module['skuId'])) {
            throw new RuntimeException('That Module is not active on this site.');
        }

        $packageResult = $this->maybeApplyModulePackage($module['skuId'], $actorUserId);
        $packageSucceeded = $this->packageInstallSucceeded((string) ($packageResult['status'] ?? ''));
        $this->auditRepository->append('module_package_install.' . ($packageSucceeded ? 'succeeded' : 'failed'), $packageSucceeded ? 'ok' : 'failed', (string) ($packageResult['status'] ?? ''), [
            'environment' => $snapshot->environment,
            'moduleSlug' => $module['slug'],
            'skuId' => $module['skuId'],
            'packageHandoff' => $packageResult,
        ], $actorUserId);

        $packageStatus = (string) ($packageResult['status'] ?? '');
        $outcome = match ($packageStatus) {
            'upgraded' => 'upgraded',
            'already_installed' => 'already_current',
            'installed', 'reconciled' => 'installed',
            default => 'install_failed',
        };
        return [
            'outcome' => $outcome,
            'moduleSlug' => $module['slug'],
            'auditEventId' => '',
            'operatorMessage' => $this->packageStatusMessage($module['displayName'], $packageResult),
            'packageHandoff' => $packageResult,
        ];
    }

    /**
     * @return array{outcome:string, moduleSlug:null|string, auditEventId:string, operatorMessage:string}
     */
    public function redeemActivationToken(string $activationToken, string $actorUserId = ''): array
    {
        $token = trim($activationToken);
        if ($token === '') {
            throw new InvalidArgumentException('Activation token is required.');
        }
        if (!class_exists(MyronMarketplaceActivationToken::class)) {
            require_once __DIR__ . '/MarketplaceActivationToken.php';
        }

        try {
            $claims = MyronMarketplaceActivationToken::verify($token);
            $this->auditRepository->append('module_redeem.token_accepted', 'ok', '', [
                'environment' => $this->environment(),
                'keyPrefix' => $this->keyPrefix((string) $claims['licenseKey']),
                'keyMask' => $this->maskKey((string) $claims['licenseKey']),
                'skuId' => (string) $claims['skuId'],
                'stage' => 'marketplace_activation_token',
            ], $actorUserId);

            return $this->redeemKey((string) $claims['licenseKey'], $actorUserId, (string) $claims['skuId']);
        } catch (Throwable $exception) {
            $this->auditRepository->append('module_redeem.token_rejected', 'denied', 'invalid_activation_token', [
                'environment' => $this->environment(),
                'stage' => 'marketplace_activation_token',
            ], $actorUserId);
            throw $exception;
        }
    }

    private function looksLikeLicenseKey(string $key): bool
    {
        $compact = preg_replace('/[^A-Za-z0-9]/', '', $key) ?? '';
        return strlen($compact) >= 12
            && strlen($compact) <= 96
            && preg_match('/^[A-Za-z0-9][A-Za-z0-9_-]*$/', str_replace('-', '_', $key)) === 1;
    }

    private function keyPrefix(string $key): string
    {
        $compact = preg_replace('/[^A-Za-z0-9]/', '', $key) ?? '';
        return substr($compact, 0, 4);
    }

    private function maskKey(string $key): string
    {
        $compact = preg_replace('/[^A-Za-z0-9]/', '', $key) ?? '';
        if ($compact === '') {
            return '';
        }
        if (strlen($compact) <= 8) {
            return str_repeat('*', max(4, strlen($compact)));
        }

        return substr($compact, 0, 4) . '-...' . substr($compact, -4);
    }

    private function environment(): string
    {
        $raw = strtolower(trim((string) (getenv('MYRON_APP_ENV') ?: 'production')));
        return match ($raw) {
            'local', 'localhost' => 'localhost',
            'dev', 'development' => 'development',
            'stage', 'staging' => 'staging',
            default => 'production',
        };
    }

    private function siteUrl(): string
    {
        if (function_exists('myronAppUrl')) {
            return myronAppUrl();
        }

        return 'https://myroncms:8890';
    }

    private function snapshotHasSku(MyronEntitlementSnapshot $snapshot, string $skuId): bool
    {
        foreach ($snapshot->activeSkus as $sku) {
            if ((string) ($sku['id'] ?? '') === $skuId) {
                return true;
            }
        }

        return false;
    }

    /**
     * @return array{status:string,path:string,error?:string}
     */
    private function maybeApplyModulePackage(string $skuId, string $actorUserId): array
    {
        $path = $this->packagePathForSku($skuId);
        $downloaded = false;
        if ($path === '') {
            if ($this->packageClient instanceof MyronRemoteModulePackageClient) {
                try {
                    $remote = $this->packageClient->downloadPackageForSku($skuId);
                    if (($remote['status'] ?? '') !== 'downloaded' || trim((string) ($remote['path'] ?? '')) === '') {
                        return [
                            'status' => (string) ($remote['status'] ?? 'remote_package_unavailable'),
                            'path' => '',
                            'reason' => (string) ($remote['reason'] ?? ''),
                        ];
                    }
                    $path = (string) $remote['path'];
                    $downloaded = true;
                } catch (Throwable $exception) {
                    return ['status' => 'remote_package_unavailable', 'path' => '', 'error' => substr($exception->getMessage(), 0, 160)];
                }
            } else {
                return ['status' => 'package_path_not_configured', 'path' => ''];
            }
        }
        if (!is_file($path)) {
            return ['status' => 'package_path_missing', 'path' => $path];
        }
        if (!function_exists('myronApplicationUpdateService')) {
            return ['status' => 'package_installer_unavailable', 'path' => $path];
        }

        try {
            $result = myronApplicationUpdateService()->applyPackagePath($path, $actorUserId);
            $installMode = (string) ($result['installMode'] ?? 'installed');
            $status = match ($installMode) {
                'already_installed' => 'already_installed',
                'reconciled_existing' => 'reconciled',
                'reconciled_upgrade', 'upgraded' => 'upgraded',
                default => 'installed',
            };
            return ['status' => $status, 'path' => $downloaded ? '' : $path, 'delivery' => $downloaded ? 'remote' : 'local'];
        } catch (Throwable $exception) {
            if (str_starts_with($exception->getMessage(), 'target_already_installed:')) {
                return ['status' => 'source_exists_untrusted', 'path' => $downloaded ? '' : $path, 'delivery' => $downloaded ? 'remote' : 'local'];
            }
            if (str_starts_with($exception->getMessage(), 'package_target_conflict:')) {
                return [
                    'status' => 'package_target_conflict',
                    'path' => $downloaded ? '' : $path,
                    'delivery' => $downloaded ? 'remote' : 'local',
                    'error' => substr($exception->getMessage(), 0, 160),
                ];
            }
            $status = $this->classifyPackageInstallFailure($exception->getMessage());
            return [
                'status' => $status,
                'path' => $downloaded ? '' : $path,
                'delivery' => $downloaded ? 'remote' : 'local',
                'error' => $this->safePackageInstallError($exception->getMessage()),
            ];
        } finally {
            if ($downloaded && is_file($path)) {
                @unlink($path);
            }
        }
    }

    /**
     * @return array{status:string,results:array<string,array<string,mixed>>}
     */
    private function maybeApplyActiveModulePackages(MyronEntitlementSnapshot $snapshot, string $actorUserId): array
    {
        $results = [];
        foreach ($snapshot->activeSkus as $sku) {
            $skuId = trim((string) ($sku['id'] ?? ''));
            if (!$this->isFirstPartyModuleSku($skuId)) {
                continue;
            }
            $results[$skuId] = $this->maybeApplyModulePackage($skuId, $actorUserId);
        }

        // Any active paid Module unlocks the full site-type catalog (APO-396). Install the
        // catalog package alongside module packages so the non-core site types register on this
        // install. Skipped silently when no catalog package is configured for this environment.
        if ($results !== []) {
            $catalog = $this->maybeApplyCatalogPackage($actorUserId);
            if (($catalog['status'] ?? '') !== 'catalog_not_configured') {
                $results['site-types-catalog'] = $catalog;
            }
        }

        if ($results === []) {
            return ['status' => 'no_active_module_packages', 'results' => []];
        }
        $statuses = array_map(static fn (array $result): string => (string) ($result['status'] ?? ''), $results);
        if (count(array_filter($statuses, fn (string $status): bool => $this->packageInstallSucceeded($status))) === count($statuses)) {
            return ['status' => 'installed', 'results' => $results];
        }
        if (count(array_filter($statuses, fn (string $status): bool => $this->packageInstallSucceeded($status))) > 0) {
            return ['status' => 'partially_installed', 'results' => $results];
        }

        return ['status' => $statuses[0] ?? 'package_path_not_configured', 'results' => $results];
    }

    /**
     * @return array{skuId:string,slug:string,displayName:string}
     */
    private function moduleDefinitionForSku(string $skuId): array
    {
        $skuId = trim($skuId);
        $definitions = [
            'pro-funnels' => ['skuId' => 'pro-funnels', 'slug' => 'funnels', 'displayName' => 'Funnels'],
            'pro-lms' => ['skuId' => 'pro-lms', 'slug' => 'lms', 'displayName' => 'LMS'],
            'pro-commerce' => ['skuId' => 'pro-commerce', 'slug' => 'commerce', 'displayName' => 'Commerce'],
            'pro-devops' => ['skuId' => 'pro-devops', 'slug' => 'devops', 'displayName' => 'Pro Operations'],
            'pro-byok-ai' => ['skuId' => 'pro-byok-ai', 'slug' => 'byok_ai', 'displayName' => 'BYOK LLM'],
        ];

        if (isset($definitions[$skuId])) {
            return $definitions[$skuId];
        }

        throw new InvalidArgumentException('Unsupported module SKU: ' . $skuId);
    }

    /**
     * Install the site-types catalog package (APO-396). The apply path enforces the
     * site_types.catalog entitlement gate, so this is a no-op-by-rejection on installs that
     * are not entitled; on entitled installs the package reseeds and registers the catalog
     * site types. Idempotent across repeat/multi-module activations (re-apply overwrites).
     *
     * @return array{status:string,path:string,error?:string}
     */
    private function maybeApplyCatalogPackage(string $actorUserId): array
    {
        $path = $this->catalogPackagePath();
        $downloaded = false;
        if ($path === '') {
            if ($this->packageClient instanceof MyronRemoteModulePackageClient && method_exists($this->packageClient, 'downloadCatalogPackage')) {
                try {
                    $remote = $this->packageClient->downloadCatalogPackage();
                    if (($remote['status'] ?? '') !== 'downloaded' || trim((string) ($remote['path'] ?? '')) === '') {
                        return ['status' => (string) ($remote['status'] ?? 'remote_package_unavailable'), 'path' => ''];
                    }
                    $path = (string) $remote['path'];
                    $downloaded = true;
                } catch (Throwable $exception) {
                    return ['status' => 'remote_package_unavailable', 'path' => '', 'error' => substr($exception->getMessage(), 0, 160)];
                }
            } else {
                return ['status' => 'catalog_not_configured', 'path' => ''];
            }
        }
        if (!is_file($path)) {
            return ['status' => 'package_path_missing', 'path' => $path];
        }
        if (!function_exists('myronApplicationUpdateService')) {
            return ['status' => 'package_installer_unavailable', 'path' => $path];
        }

        try {
            $result = myronApplicationUpdateService()->applyPackagePath($path, $actorUserId);
            $installMode = (string) ($result['installMode'] ?? 'installed');
            $status = match ($installMode) {
                'already_installed' => 'already_installed',
                'reconciled_existing' => 'reconciled',
                default => 'installed',
            };
            return ['status' => $status, 'path' => $downloaded ? '' : $path];
        } catch (Throwable $exception) {
            return [
                'status' => $this->classifyPackageInstallFailure($exception->getMessage()),
                'path' => $downloaded ? '' : $path,
                'error' => $this->safePackageInstallError($exception->getMessage()),
            ];
        } finally {
            if ($downloaded && is_file($path)) {
                @unlink($path);
            }
        }
    }

    private function catalogPackagePath(): string
    {
        $json = trim((string) getenv('MYRON_MODULE_PACKAGE_PATHS_JSON'));
        if ($json !== '') {
            $decoded = json_decode($json, true);
            if (is_array($decoded)) {
                $mappedPath = trim((string) ($decoded['site-types-catalog'] ?? ''));
                if ($mappedPath !== '') {
                    return $mappedPath;
                }
            }
        }

        return trim((string) getenv('MYRON_SITE_TYPES_CATALOG_PACKAGE_PATH'));
    }

    private function packagePathForSku(string $skuId): string
    {
        $json = trim((string) getenv('MYRON_MODULE_PACKAGE_PATHS_JSON'));
        if ($json !== '') {
            $decoded = json_decode($json, true);
            if (is_array($decoded)) {
                $mappedPath = trim((string) ($decoded[$skuId] ?? ''));
                if ($mappedPath !== '') {
                    return $mappedPath;
                }
            }
        }

        $slug = preg_replace('/^pro-/', '', $skuId) ?? $skuId;
        $envName = 'MYRON_' . strtoupper(str_replace('-', '_', $slug)) . '_PACKAGE_PATH';
        return trim((string) getenv($envName));
    }

    /**
     * @param array{status:string,path:string,error?:string} $packageResult
     */
    private function packageStatusMessage(string $displayName, array $packageResult): string
    {
        return match ((string) ($packageResult['status'] ?? '')) {
            'installed' => $displayName . ' license activated and the module package was installed.',
            'reconciled' => $displayName . ' license activated and the existing module package files were verified against the signed package.',
            'package_path_missing' => $displayName . ' license activated, but the configured signed package could not be found. Check the Module package path and retry installation.',
            'package_installer_unavailable' => $displayName . ' license activated, but this install cannot apply Module packages yet. Reopen Modules after the updater is available.',
            'install_failed' => $displayName . ' license activated, but package installation failed. Review the latest redemption event before retrying.',
            'package_trust_root_missing' => $displayName . ' license activated, but this Core build does not trust the package signing key. Update Core or contact support before retrying.',
            'package_trust_root_invalid' => $displayName . ' license activated, but the package trust root is invalid. Update Core or contact support before retrying.',
            'package_signature_algorithm_unsupported' => $displayName . ' license activated, but this Core build cannot verify the package signature algorithm. Update Core before retrying.',
            'package_signature_invalid' => $displayName . ' license activated, but the signed Module package could not be verified. Do not install this package.',
            'package_checksum_mismatch' => $displayName . ' license activated, but the package checksum did not match its manifest. Do not install this package.',
            'package_crypto_unavailable' => $displayName . ' license activated, but this server is missing the crypto support required to verify Module packages.',
            'package_legacy_hmac_key_unavailable' => $displayName . ' license activated, but this package uses legacy HMAC signing and no test signing key is configured. Use a current first-party signed package.',
            'already_installed' => $displayName . ' package is already installed on this site.',
            'upgraded' => $displayName . ' package was upgraded. Start a clean request to load its new runtime and migrations.',
            'source_exists_untrusted' => $displayName . ' source files already exist, but no trusted package record is present. Reinstall from a signed matching package to reconcile provenance.',
            'package_target_conflict' => $displayName . ' license activated, but existing module files do not match the signed package. Review the latest redemption event before retrying.',
            'package_version_downgrade' => $displayName . ' package was not changed because Module downgrades are not allowed.',
            'package_version_hash_conflict' => $displayName . ' package was not changed because the same version has a different trusted hash.',
            'partially_installed' => $displayName . ' license activated. Some granted Module packages installed; review Installed Modules for any pending packages.',
            'remote_package_endpoint_not_configured' => $displayName . ' license activated. Configure the Module package service endpoint before installing package code.',
            'remote_package_unavailable', 'entitlement_denied', 'integrity_denied' => $displayName . ' license activated, but the signed Module package is not available for this install yet.',
            'package_path_not_configured' => $displayName . ' license activated. Configure a signed ' . $displayName . ' package path before installing it on this site.',
            default => $displayName . ' license activated. Install the signed ' . $displayName . ' package when it is available for this site.',
        };
    }

    private function packageInstallSucceeded(string $status): bool
    {
        return in_array($status, ['installed', 'upgraded', 'already_installed', 'reconciled'], true);
    }

    private function isFirstPartyModuleSku(string $skuId): bool
    {
        return in_array($skuId, ['pro-funnels', 'pro-lms', 'pro-commerce', 'pro-devops', 'pro-byok-ai'], true);
    }

    private function classifyPackageInstallFailure(string $message): string
    {
        return match (true) {
            str_contains($message, 'marketplace_trust_root_missing') => 'package_trust_root_missing',
            str_contains($message, 'marketplace_trust_root_invalid') => 'package_trust_root_invalid',
            str_contains($message, 'envelope_signature_algorithm_unsupported') => 'package_signature_algorithm_unsupported',
            str_contains($message, 'envelope_signature_invalid') => 'package_signature_invalid',
            str_contains($message, 'checksum mismatch'), str_contains($message, 'Payload checksum mismatch') => 'package_checksum_mismatch',
            str_contains($message, 'marketplace_crypto_unavailable') => 'package_crypto_unavailable',
            str_contains($message, 'marketplace_hmac_key_unavailable') => 'package_legacy_hmac_key_unavailable',
            str_contains($message, 'capability_not_entitled') => 'entitlement_denied',
            str_contains($message, 'package_version_downgrade') => 'package_version_downgrade',
            str_contains($message, 'package_version_hash_conflict') => 'package_version_hash_conflict',
            str_contains($message, 'package_target_conflict') => 'package_target_conflict',
            default => 'install_failed',
        };
    }

    private function safePackageInstallError(string $message): string
    {
        foreach ([
            'marketplace_trust_root_missing',
            'marketplace_trust_root_invalid',
            'envelope_signature_algorithm_unsupported',
            'envelope_signature_invalid',
            'package_checksum_mismatch',
            'marketplace_crypto_unavailable',
            'marketplace_hmac_key_unavailable',
            'capability_not_entitled',
            'package_version_downgrade',
            'package_version_hash_conflict',
            'package_target_conflict',
        ] as $code) {
            if (str_contains($message, $code)) {
                return $code;
            }
        }

        if (str_contains($message, 'Payload checksum mismatch') || str_contains($message, 'checksum mismatch')) {
            return 'package_checksum_mismatch';
        }

        return 'package_install_failed';
    }
}
