import path from "path";
import fs from "fs";
import esbuild from "esbuild";
import { createWorker, FsWatcher, pathx } from "@simplysm/core-node";
import { err as errNs } from "@simplysm/core-common";
import { formatEsbuildMessages } from "../utils/output-utils.js";
import { parseTsconfig, getPackageSourceFiles, } from "../utils/tsconfig.js";
import { createServerEsbuildOptions, writeChangedOutputFiles, } from "../esbuild/esbuild-config.js";
import { collectAllExternals, generateProductionFiles } from "../deps/server-externals/server-production-files.js";
import { SdTsCompiler } from "../ts-compiler/SdTsCompiler.js";
import { createTscPlugin } from "../esbuild/esbuild-tsc-plugin.js";
import { createWorkerBundlePlugin } from "../esbuild/esbuild-worker-plugin.js";
import { setupWorkerLifecycle } from "./shared-worker-lifecycle.js";
import { buildWatchPaths } from "./build-watch-paths.js";
import { copyPublicFiles, watchPublicFiles } from "../utils/copy-public.js";
import * as esbuildCtx from "./server-esbuild-context.js";
import { startServerWatchLoop } from "./server-watch-manager.js";
//#endregion
//#region Resource Management
/** public 파일 감시자 (정리 대상) */
let publicWatcher;
/** 소스 + 스코프 패키지 감시자 (정리 대상) */
let srcWatcher;
async function cleanup() {
    await esbuildCtx.dispose();
    const watcherToClose = publicWatcher;
    publicWatcher = undefined;
    const srcWatcherToClose = srcWatcher;
    srcWatcher = undefined;
    if (watcherToClose != null) {
        await watcherToClose.close();
    }
    if (srcWatcherToClose != null) {
        await srcWatcherToClose.close();
    }
}
const { logger, guardStartWatch } = setupWorkerLifecycle("server-build", cleanup);
//#endregion
//#region Worker
/**
 * 일회성 빌드 (프로덕션)
 */
async function build(info) {
    const mainJsPath = pathx.posixResolve(info.pkgDir, "dist", "main.js");
    logger.debug(`[${info.name}] server worker build 시작 (js: ${info.output.js}, dts: ${info.output.dts})`);
    try {
        // tsconfig 파싱
        const parsedConfig = parseTsconfig(info.pkgDir);
        const entryPoints = getPackageSourceFiles(info.pkgDir, parsedConfig);
        // 외부 모듈 수집
        const { bundleExternals, prodDependencies } = collectAllExternals(info.pkgDir, info.externals);
        let jsResult;
        let tscErrors;
        let tscDiagnostics;
        let lint;
        if (info.output.js) {
            // js=true: tsc 플러그인 통합 — 단일 esbuild.build() 호출
            const tscPlugin = createTscPlugin({
                pkgDir: info.pkgDir,
                cwd: info.cwd,
                output: { dts: info.output.dts },
                env: info.output.env,
                includeTests: info.output.includeTests,
                lint: info.output.lint,
            });
            const esbuildOptions = createServerEsbuildOptions({
                pkgDir: info.pkgDir,
                entryPoints,
                env: info.env,
                external: bundleExternals,
            });
            jsResult = await esbuild.build({ ...esbuildOptions, plugins: [createWorkerBundlePlugin(), tscPlugin.plugin] })
                .then(async (result) => {
                if (result.outputFiles) {
                    await writeChangedOutputFiles(result.outputFiles);
                }
                const errors = formatEsbuildMessages(result.errors, "error");
                const warnings = formatEsbuildMessages(result.warnings, "warning");
                return {
                    success: result.errors.length === 0,
                    errors: errors.length > 0 ? errors : undefined,
                    warnings: warnings.length > 0 ? warnings : undefined,
                };
            })
                .catch((err) => ({
                success: false,
                errors: [errNs.message(err)],
                warnings: undefined,
            }));
            tscErrors = tscPlugin.getErrors() ?? [];
            tscDiagnostics = tscPlugin.getDiagnostics();
            lint = tscPlugin.getLintResult();
        }
        else {
            // js=false: SdTsCompiler 직접 호출 (플러그인 경유 불가)
            const compiler = new SdTsCompiler({
                pkgDir: info.pkgDir,
                cwd: info.cwd,
                output: { js: false, dts: info.output.dts },
                env: info.output.env,
                includeTests: info.output.includeTests,
                lint: info.output.lint,
            });
            const tscResult = await compiler.compileAsync();
            jsResult = { success: true, errors: undefined, warnings: undefined };
            tscErrors = tscResult.errors ?? [];
            tscDiagnostics = tscResult.diagnostics;
            lint = tscResult.lint;
        }
        // JS 출력이 요청된 경우에만 프로덕션 아티팩트 생성
        if (info.output.js) {
            const confDistPath = path.join(info.pkgDir, "dist", ".config.json");
            fs.writeFileSync(confDistPath, JSON.stringify(info.configs ?? {}, undefined, 2));
            await copyPublicFiles(info.pkgDir, false);
            generateProductionFiles(info, prodDependencies);
        }
        const allErrors = [...(jsResult.errors ?? []), ...tscErrors];
        const tscSuccess = tscErrors.length === 0;
        logger.debug(`[${info.name}] server worker build 완료 (js: ${jsResult.success}, tsc: ${tscSuccess})`);
        return {
            build: {
                success: jsResult.success && tscSuccess,
                errors: allErrors.length > 0 ? allErrors : undefined,
                warnings: jsResult.warnings,
                diagnostics: tscDiagnostics,
            },
            lint,
            mainJsPath,
        };
    }
    catch (err) {
        const message = errNs.message(err);
        const stack = err instanceof Error ? err.stack : undefined;
        logger.debug(`[${info.name}] server worker build 예외: ${message}`);
        if (stack != null) {
            logger.debug(`[${info.name}] 스택 트레이스:\n${stack}`);
        }
        return {
            build: { success: false, errors: [message], diagnostics: [] },
            mainJsPath,
        };
    }
}
/**
 * watch 모드 시작
 */
async function startWatch(info) {
    guardStartWatch();
    logger.debug(`[${info.name}] server worker startWatch 시작`);
    // watch 모드 로컬 상태 (클로저로 관리)
    // js=false 경로에서 SdTsCompiler 인스턴스를 유지하여 증분 빌드를 지원한다
    let watchCompiler;
    /**
     * esbuild + tsc 리빌드 (watch 모드)
     * js=true: esbuildCtx.rebuild() 단일 호출 (tsc 플러그인 자동 트리거)
     * js=false: SdTsCompiler.compileAsync() 직접 호출
     */
    async function rebuildAll() {
        logger.debug(`[${info.name}] rebuildAll 시작`);
        const mainJsPath = pathx.posixResolve(info.pkgDir, "dist", "main.js");
        let jsResult;
        let tscErrors;
        let lint;
        if (info.output.js) {
            // js=true: esbuildCtx.rebuild() 단일 호출 (tsc 에러 자동 병합)
            const rebuildResult = await esbuildCtx.rebuild();
            jsResult = rebuildResult ?? { success: true, errors: undefined, warnings: undefined };
            tscErrors = [];
            lint = esbuildCtx.getTscLintResult();
        }
        else {
            // js=false: SdTsCompiler 직접 호출
            if (watchCompiler == null) {
                watchCompiler = new SdTsCompiler({
                    pkgDir: info.pkgDir,
                    cwd: info.cwd,
                    output: { js: false, dts: info.output.dts },
                    env: info.output.env,
                    includeTests: info.output.includeTests,
                    lint: info.output.lint,
                });
            }
            const tscResult = await watchCompiler.compileAsync();
            jsResult = { success: true, errors: undefined, warnings: undefined };
            tscErrors = tscResult.errors ?? [];
            lint = tscResult.lint;
        }
        const allErrors = [...(jsResult.errors ?? []), ...tscErrors];
        const allSuccess = jsResult.success && tscErrors.length === 0;
        logger.debug(`[${info.name}] rebuildAll 완료`);
        return {
            build: {
                success: allSuccess,
                errors: allErrors.length > 0 ? allErrors : undefined,
                warnings: jsResult.warnings,
            },
            lint,
            mainJsPath,
        };
    }
    try {
        const parsedConfig = parseTsconfig(info.pkgDir);
        const entryPoints = getPackageSourceFiles(info.pkgDir, parsedConfig);
        // 외부 모듈 수집 (watch 모드용 — watch manager가 자체 캐시를 유지)
        const { bundleExternals: cachedExternal } = collectAllExternals(info.pkgDir, info.externals);
        // esbuild 컨텍스트 생성 (JS 출력 필요 시, tsc 플러그인 포함)
        if (info.output.js) {
            await esbuildCtx.createContext({
                pkgDir: info.pkgDir,
                entryPoints,
                env: info.env,
                external: cachedExternal,
                tsc: {
                    cwd: info.cwd,
                    output: { dts: info.output.dts },
                    env: info.output.env,
                    includeTests: info.output.includeTests,
                    lint: info.output.lint,
                },
            });
        }
        // 초기 빌드
        sender.send("buildStart", {});
        const initialResult = await rebuildAll();
        // 첫 빌드 시 .config.json 작성
        const confDistPath = path.join(info.pkgDir, "dist", ".config.json");
        fs.writeFileSync(confDistPath, JSON.stringify(info.configs ?? {}, undefined, 2));
        sender.send("build", initialResult);
        // public/ + public-dev/ 감시
        publicWatcher = await watchPublicFiles(info.pkgDir, true);
        // 의존성 기반 감시 경로 수집 + FsWatcher 감시 루프 시작
        const { watchPaths } = buildWatchPaths({
            pkgDir: info.pkgDir,
            cwd: info.cwd,
            srcGlobs: ["*"],
            replaceDeps: info.replaceDeps,
        });
        srcWatcher = await startServerWatchLoop({
            info,
            watchPaths,
            logger,
            initialExternals: cachedExternal,
            onBuildStart: () => sender.send("buildStart", {}),
            onBuild: (result) => sender.send("build", result),
            onError: (message) => sender.send("error", { message }),
            rebuild: () => rebuildAll(),
        });
    }
    catch (err) {
        sender.send("error", { message: errNs.message(err) });
    }
}
/**
 * watch 중지
 */
async function stopWatch() {
    await cleanup();
}
const sender = createWorker({
    build,
    startWatch,
    stopWatch,
});
export default sender;
//#endregion
//# sourceMappingURL=server-build.worker.js.map