<?php

declare(strict_types=1);

namespace Acms\Services\Asset;

/**
 * AssetDefinition の deps（依存ハンドル）をトポロジカルソートする純粋関数群。
 *
 * グローバル状態・I/O に依存しないため、AssetManager から分離して単体テストできるようにしている。
 */
final class DependencyResolver
{
    /**
     * @param array<string, AssetDefinition> $definitions handle => AssetDefinition（登録順を維持した配列）
     * @return array{sorted: AssetDefinition[], warnings: string[]}
     */
    public static function sort(array $definitions): array
    {
        $warnings = [];

        // 存在しないハンドルへの deps は除外し、警告として記録する。
        $remainingDeps = [];
        foreach ($definitions as $handle => $definition) {
            $deps = [];
            foreach ($definition->deps as $dep) {
                if (!isset($definitions[$dep])) {
                    $warnings[] = "Asset '{$handle}' depends on unknown handle '{$dep}'. Ignoring this dependency.";
                    continue;
                }
                $deps[$dep] = true;
            }
            $remainingDeps[$handle] = $deps;
        }

        $sorted = [];
        $handles = array_keys($definitions);

        while ($remainingDeps !== []) {
            $readyHandle = null;
            foreach ($handles as $handle) {
                if (!isset($remainingDeps[$handle])) {
                    continue;
                }
                if ($remainingDeps[$handle] === []) {
                    $readyHandle = $handle;
                    break;
                }
            }

            if ($readyHandle === null) {
                // 循環依存: 残っている中で最も登録順が早いハンドルを強制的に確定させ、打ち切って続行する。
                foreach ($handles as $handle) {
                    if (isset($remainingDeps[$handle])) {
                        $readyHandle = $handle;
                        break;
                    }
                }
                assert($readyHandle !== null);
                $cycle = array_keys($remainingDeps[$readyHandle]);
                $warnings[] = "Circular dependency detected involving '{$readyHandle}' and " . implode(', ', $cycle) . '. Breaking the cycle.';
            }

            $sorted[] = $definitions[$readyHandle];
            unset($remainingDeps[$readyHandle]);

            foreach ($remainingDeps as $handle => $deps) {
                unset($remainingDeps[$handle][$readyHandle]);
            }
        }

        return ['sorted' => $sorted, 'warnings' => $warnings];
    }
}
