<?php

declare(strict_types=1);

namespace App\Services\SupportCenter;

use App\Exceptions\SupportCenter\HostSystemUpdateFileExecutionException;
use App\Exceptions\SupportCenter\HostSystemUpdateStagingException;
use App\Models\HostSystemUpdateBackup;
use App\Models\HostSystemUpdateExecution;
use App\Models\HostSystemUpdatePackage;

/**
 * 解析执行任务上下文（包、manifest、staging、备份），供文件执行层复用。
 */
final class HostSystemUpdateExecutionContextResolver
{
    public function resolveReadyPackage(HostSystemUpdateExecution $execution): HostSystemUpdatePackage
    {
        $package = $execution->package;

        if (! $package instanceof HostSystemUpdatePackage) {
            throw HostSystemUpdateFileExecutionException::prerequisitesNotMet('package_missing');
        }

        if (! $package->verified
            || $package->download_status !== HostSystemUpdatePackage::STATUS_VERIFIED
            || $package->manifest_status !== HostSystemUpdatePackage::MANIFEST_STATUS_VALID) {
            throw HostSystemUpdateFileExecutionException::prerequisitesNotMet('package_not_ready');
        }

        return $package;
    }

    /**
     * @return array<string, mixed>
     */
    public function manifest(HostSystemUpdateExecution $execution): array
    {
        $package = $this->resolveReadyPackage($execution);
        $manifest = $package->manifest;

        if (! is_array($manifest) || $manifest === []) {
            throw HostSystemUpdateFileExecutionException::prerequisitesNotMet('manifest_missing');
        }

        return $manifest;
    }

    public function stagingDirectory(HostSystemUpdateExecution $execution): string
    {
        $directory = HostSystemUpdateWorkspaceStorage::stagingDirectory($execution->execution_no);

        if (! is_dir($directory)) {
            throw HostSystemUpdateFileExecutionException::stagingMissing();
        }

        return $directory;
    }

    public function resolveCompletedFileSnapshotBackup(HostSystemUpdateExecution $execution): HostSystemUpdateBackup
    {
        $backup = $execution->backups()
            ->where('backup_type', HostSystemUpdateBackup::TYPE_FILE_SNAPSHOT)
            ->where('status', HostSystemUpdateBackup::STATUS_COMPLETED)
            ->latest('id')
            ->first();

        if (! $backup instanceof HostSystemUpdateBackup) {
            throw HostSystemUpdateFileExecutionException::backupNotCompleted();
        }

        return $backup;
    }

    /**
     * @return list<string>
     */
    public function backedUpPaths(HostSystemUpdateExecution $execution): array
    {
        $backup = $this->resolveCompletedFileSnapshotBackup($execution);
        $paths = $backup->included_paths;

        if (! is_array($paths)) {
            return [];
        }

        return array_values(array_filter($paths, static fn (mixed $path): bool => is_string($path) && $path !== ''));
    }

    public function stagedFilePath(string $stagingDirectory, string $relativePath): string
    {
        $normalized = $this->protectedPathGuard()->normalizeRelativePath($relativePath);

        if ($normalized === null) {
            throw HostSystemUpdateStagingException::protectedUpdateTarget($relativePath, 'files');
        }

        return $stagingDirectory.DIRECTORY_SEPARATOR.str_replace('/', DIRECTORY_SEPARATOR, $normalized);
    }

    private function protectedPathGuard(): HostSystemUpdateProtectedPathGuard
    {
        return app(HostSystemUpdateProtectedPathGuard::class);
    }
}
