<?php

declare(strict_types=1);

final class MyronMarketplaceExtensionInstallService
{
    public function __construct(
        private readonly string $appRoot,
        private readonly MyronMarketplaceExtensionPackageClient $client,
        private readonly MyronUpdateApplicationService $updates,
    ) {
    }

    public function listCandidates(array $filters = []): array
    {
        $state = $this->client->list($filters);
        $items = [];
        foreach (is_array($state['items'] ?? null) ? $state['items'] : [] as $item) {
            if (!is_array($item)) {
                continue;
            }
            $namespace = trim((string) ($item['namespace'] ?? ''));
            $items[] = $item + [
                'installed' => $namespace !== '' && is_dir($this->appRoot . '/extensions/' . $namespace),
            ];
        }

        return $state + ['items' => $items];
    }

    public function install(string $packageVersionId, string $actorUserId, array $knownCandidates = []): array
    {
        $packageVersionId = trim($packageVersionId);
        if ($packageVersionId === '') {
            throw new InvalidArgumentException('Extension package selection is required.');
        }

        $candidate = $this->findCandidate($packageVersionId, $knownCandidates);
        if ($candidate === null) {
            $state = $this->client->list();
            $candidate = $this->findCandidate($packageVersionId, is_array($state['items'] ?? null) ? $state['items'] : []);
        }
        if ($candidate === null) {
            throw new RuntimeException('Extension package is no longer available for install.');
        }
        if (($candidate['installable'] ?? false) !== true || (string) ($candidate['targetType'] ?? '') !== 'extension') {
            throw new RuntimeException('Only installable Extension packages can be installed here.');
        }

        $downloadPath = $this->downloadPath($packageVersionId);
        try {
            $download = $this->client->download($candidate, $downloadPath);
            $result = $this->updates->applyPackagePath((string) $download['path'], $actorUserId);

            return [
                'candidate' => $this->safeCandidateSummary($candidate),
                'download' => [
                    'sha256' => (string) $download['sha256'],
                    'sizeBytes' => (int) $download['sizeBytes'],
                ],
                'update' => $result,
                'autoGrantScopes' => false,
                'autoEnabled' => false,
            ];
        } finally {
            if (is_file($downloadPath)) {
                unlink($downloadPath);
            }
        }
    }

    private function findCandidate(string $packageVersionId, array $items): ?array
    {
        foreach ($items as $item) {
            if (is_array($item) && (string) ($item['packageVersionId'] ?? '') === $packageVersionId) {
                return $item;
            }
        }

        return null;
    }

    private function downloadPath(string $packageVersionId): string
    {
        $safe = preg_replace('/[^A-Za-z0-9_.-]+/', '-', $packageVersionId) ?: 'extension-package';
        $dir = $this->appRoot . '/storage/updates/marketplace-downloads';
        if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) {
            throw new RuntimeException('Unable to create Extension package download workspace.');
        }

        return $dir . '/' . $safe . '-' . bin2hex(random_bytes(4)) . '.zip';
    }

    private function safeCandidateSummary(array $candidate): array
    {
        return [
            'packageVersionId' => (string) ($candidate['packageVersionId'] ?? ''),
            'packageId' => (string) ($candidate['packageId'] ?? ''),
            'version' => (string) ($candidate['version'] ?? ''),
            'namespace' => (string) ($candidate['namespace'] ?? ''),
            'displayName' => (string) ($candidate['displayName'] ?? ''),
        ];
    }
}
