import { jsx as _jsx } from "react/jsx-runtime";
import { createGhostflowSDK, LaunchdService } from "@ghostflow/client";
import { Data, Duration, Effect, Schedule } from "effect";
import { render } from "ink";
import { resolveDaemonPath } from "../resolver.js";
import { ensureSetup, isSetupComplete } from "../setup.js";
import { App } from "./App.js";
// ============================================================================
// Errors
// ============================================================================
/** Error when daemon fails to become ready within timeout */
export class DaemonNotReadyError extends Data.TaggedError("DaemonNotReadyError") {
}
/** Error when TUI rendering fails */
export class TuiRenderError extends Data.TaggedError("TuiRenderError") {
}
/** Internal error for daemon readiness polling (not exported - only used within retry loop) */
class DaemonNotRunningYet extends Data.TaggedError("DaemonNotRunningYet") {
}
/** Error when SDK initialization fails */
export class SdkInitError extends Data.TaggedError("SdkInitError") {
}
// ============================================================================
// Constants
// ============================================================================
/** Maximum attempts to wait for daemon readiness */
const DAEMON_READY_MAX_ATTEMPTS = 10;
/** Delay between daemon readiness checks */
const DAEMON_READY_CHECK_INTERVAL = Duration.millis(300);
// ============================================================================
// Helpers
// ============================================================================
/** Type guard for objects with a message property */
const hasMessage = (value) => {
    if (typeof value !== "object" || value === null) {
        return false;
    }
    const message = Reflect.get(value, "message");
    return typeof message === "string";
};
/** Extract error message safely without instanceof */
const extractErrorMessage = (cause, fallback) => hasMessage(cause) ? cause.message : fallback;
// ============================================================================
// Setup
// ============================================================================
/**
 * Wait for daemon to be running by polling status.
 * More reliable than a fixed sleep since daemon startup time varies.
 */
const waitForDaemonReady = (launchd) => launchd.status().pipe(Effect.flatMap((status) => status.isRunning
    ? Effect.succeed(status)
    : Effect.fail(new DaemonNotRunningYet({}))), Effect.retry(Schedule.spaced(DAEMON_READY_CHECK_INTERVAL).pipe(Schedule.intersect(Schedule.recurs(DAEMON_READY_MAX_ATTEMPTS)))), Effect.catchAll(() => Effect.fail(new DaemonNotReadyError({
    message: "Daemon failed to start within timeout",
    attempts: DAEMON_READY_MAX_ATTEMPTS
}))));
/**
 * Ensure daemon is set up and running before starting TUI.
 * Creates config directories, plist, and starts daemon if needed.
 */
const ensureDaemonRunning = Effect.gen(function* () {
    const daemonPath = yield* resolveDaemonPath();
    // Run setup if not already complete
    yield* isSetupComplete(daemonPath).pipe(Effect.flatMap((setupDone) => setupDone ? Effect.void : ensureSetup(daemonPath)));
    // Start daemon if not running
    const launchd = yield* LaunchdService;
    yield* launchd.status().pipe(Effect.flatMap((status) => status.isRunning
        ? Effect.void
        : Effect.zipRight(launchd.load({
            daemonPath,
            workingDirectory: undefined,
            environment: undefined
        }), waitForDaemonReady(launchd))));
});
// ============================================================================
// TUI Rendering
// ============================================================================
/** ANSI escape: enter alternate screen buffer + clear + cursor home */
const ENTER_ALT_SCREEN = "\x1b[?1049h\x1b[2J\x1b[H";
/** ANSI escape: leave alternate screen buffer */
const EXIT_ALT_SCREEN = "\x1b[?1049l";
/**
 * Render TUI and wait for exit as an Effect.
 * Uses alternate screen buffer for clean terminal experience.
 */
const renderTuiEffect = (sdk) => Effect.async((resume) => {
    process.stdout.write(ENTER_ALT_SCREEN);
    const { unmount, waitUntilExit } = render(_jsx(App, { sdk: sdk }));
    waitUntilExit()
        .then(() => {
        unmount();
        process.stdout.write(EXIT_ALT_SCREEN);
        resume(Effect.succeed(undefined));
        // Exit after resume completes to allow any cleanup
        setImmediate(() => process.exit(0));
    })
        .catch((cause) => {
        unmount();
        process.stdout.write(EXIT_ALT_SCREEN);
        resume(Effect.fail(new TuiRenderError({
            message: extractErrorMessage(cause, String(cause)),
            cause
        })));
        // Exit after resume completes to allow any cleanup
        setImmediate(() => process.exit(1));
    });
});
/**
 * TUI startup as a pure Effect with resource management.
 *
 * Ensures setup is complete, daemon is running, acquires SDK,
 * renders TUI, and automatically disposes SDK on exit.
 *
 * Requires LaunchdService - provide MainLive and run with NodeRuntime.runMain.
 * Uses acquireUseRelease for clean acquire → use → release pattern.
 */
export const tui = (config) => Effect.gen(function* () {
    yield* ensureDaemonRunning;
    yield* Effect.acquireUseRelease(Effect.tryPromise({
        try: () => createGhostflowSDK(config),
        catch: (cause) => new SdkInitError({
            message: extractErrorMessage(cause, "Failed to initialize SDK"),
            cause
        })
    }), (sdk) => renderTuiEffect(sdk), (sdk) => Effect.promise(() => sdk.dispose()));
});
// Re-export App for testing
export { App } from "./App.js";
//# sourceMappingURL=index.js.map