import { spawn } from "child_process";
import { FsWatcher, pathx } from "@simplysm/core-node";
import { filterPackagesByTargets, classifyWatchPackages } from "../utils/package-classify.js";
import { printDiagnostics } from "../utils/output-utils.js";
import { createBuildEngine } from "../engines/engine-factory.js";
import { watchCopySrcFiles } from "../utils/copy-src.js";
import { BaseOrchestrator } from "./BaseOrchestrator.js";
/**
 * watch 모드 전용 Orchestrator
 *
 * 라이브러리(JS+DTS) + Scripts(watch hook) + copySrc + replaceDeps 감시
 */
export class WatchOrchestrator extends BaseOrchestrator {
    _options;
    // watch 전용 상태
    _libraryEngines = [];
    _libraryPackages = [];
    _watchHookPackages = [];
    _copySrcWatchers = [];
    _distDeleteWatchers = [];
    _watchHookWatchers = [];
    _watchHookChildren = new Map();
    constructor(options) {
        super("sd:cli:watch");
        this._options = options;
    }
    async initialize() {
        await super.initialize({
            targets: this._options.targets,
            options: this._options.options,
            dev: true,
        });
    }
    _initializeMode(config, targets) {
        // 대상으로 필터링
        const allPackages = filterPackagesByTargets(config.packages, targets);
        // watch 모드 패키지 분류
        const classified = classifyWatchPackages(allPackages, this._cwd, this._pathMap);
        this._libraryPackages = classified.libraryPackages;
        this._watchHookPackages = classified.watchHookPackages;
        // 처리할 패키지가 있는지 확인
        const totalPackages = this._libraryPackages.length + this._watchHookPackages.length;
        if (totalPackages === 0) {
            this._logger.warn("워치 대상 패키지가 없습니다.");
            return;
        }
        this._hasPackages = true;
    }
    _initializeEngines(_config) {
        // 배치 완료 핸들러 등록
        this._rebuildManager.on("batchComplete", (_completedKeys) => {
            printDiagnostics(this._resultCollector.toMap());
        });
        // 라이브러리 패키지용 BuildEngine 생성
        for (const pkg of this._libraryPackages) {
            const engine = createBuildEngine(pkg, {
                cwd: this._cwd,
                replaceDeps: this._replaceDeps,
                resultCollector: this._resultCollector,
                rebuildManager: this._rebuildManager,
            });
            this._libraryEngines.push(engine);
        }
    }
    async start() {
        if (!this._hasPackages) {
            this._logger.debug("대상 패키지 없음, start 건너뜀");
            return;
        }
        this._logger.debug("watch 모드 시작");
        // 라이브러리 패키지 dist 삭제 감지 워처
        for (const pkg of this._libraryPackages) {
            const distDir = pathx.posixResolve(pkg.dir, "dist");
            const watcher = await FsWatcher.watch([distDir]);
            watcher.onChange({ delay: 100 }, (changes) => {
                for (const c of changes) {
                    if (c.event === "unlink" || c.event === "unlinkDir") {
                        this._logger.error(`[dist-delete:${pkg.name}] ${c.event}: ${c.path}\n${new Error().stack}`);
                    }
                }
            });
            this._distDeleteWatchers.push(watcher);
        }
        // Start copySrc watchers for library packages
        for (const pkg of this._libraryPackages) {
            if (pkg.config.copySrc != null && pkg.config.copySrc.length > 0) {
                const watcher = await watchCopySrcFiles(pkg.dir, pkg.config.copySrc);
                this._copySrcWatchers.push(watcher);
            }
        }
        // Start all engines
        const total = this._libraryEngines.length;
        this._logger.start(`초기 빌드 실행 중... (${total}개 패키지)`);
        let completed = 0;
        const watchPromises = this._libraryEngines.map(async (engine, i) => {
            const pkgName = this._libraryPackages[i].name;
            await engine.startWatch({ js: true, dts: true, lint: false, includeTests: false });
            completed++;
            this._logger.info(`  [${completed}/${total}] ${pkgName} 완료`);
        });
        await Promise.allSettled(watchPromises);
        this._logger.success("초기 빌드 실행 완료");
        // Print initial build results
        printDiagnostics(this._resultCollector.toMap());
        // Start watch hook watchers for scripts+watch packages
        for (const pkg of this._watchHookPackages) {
            const watchConfig = pkg.config.watch;
            const watchTargets = watchConfig.target.map((t) => pathx.posixResolve(pkg.dir, t));
            // Run initial hook
            this._runWatchHookCmd(pkg.name, pkg.dir, watchConfig.cmd, watchConfig.args);
            // Start watching
            const watcher = await FsWatcher.watch(watchTargets);
            watcher.onChange({ delay: 300 }, () => {
                this._runWatchHookCmd(pkg.name, pkg.dir, watchConfig.cmd, watchConfig.args);
            });
            this._watchHookWatchers.push(watcher);
            this._logger.success(`워치 훅 시작됨: ${pkg.name}`);
        }
    }
    async _shutdownMode() {
        const shutdownTasks = [];
        // 모든 엔진 중지
        shutdownTasks.push(...this._libraryEngines.map((e) => e.stop()));
        // 워처 종료
        shutdownTasks.push(...this._copySrcWatchers.map((w) => w.close()));
        shutdownTasks.push(...this._distDeleteWatchers.map((w) => w.close()));
        shutdownTasks.push(...this._watchHookWatchers.map((w) => w.close()));
        // hook 자식 프로세스 종료
        for (const child of this._watchHookChildren.values()) {
            if (child.exitCode == null) {
                child.kill();
            }
        }
        this._watchHookChildren.clear();
        await Promise.all(shutdownTasks);
        this._copySrcWatchers = [];
        this._distDeleteWatchers = [];
        this._watchHookWatchers.length = 0;
    }
    _runWatchHookCmd(pkgName, cwd, cmd, args) {
        // Kill previous hook process if still running
        const prev = this._watchHookChildren.get(pkgName);
        if (prev != null && prev.exitCode == null) {
            prev.kill();
        }
        const child = spawn(cmd, args ?? [], { cwd, stdio: "inherit", shell: true });
        this._watchHookChildren.set(pkgName, child);
        child.on("error", (err) => {
            this._logger.error(`[${pkgName}] 워치 훅 에러: ${err.message}`);
        });
        child.on("close", (code) => {
            if (code !== 0 && code != null) {
                this._logger.warn(`[${pkgName}] 워치 훅이 코드 ${String(code)}로 종료됨`);
            }
        });
    }
}
//# sourceMappingURL=WatchOrchestrator.js.map