/**
 * First-run setup and initialization
 *
 * Handles automatic setup when `ghostflow start` is run for the first time:
 * - Creates config directory (~/.config/ghostflow or ~/Library/Application Support/ghostflow)
 * - Prompts for REPO_PATH if not configured
 * - Generates launchd plist with correct paths
 *
 * NOTE: This module currently only supports macOS. All paths (config dir, LaunchAgents,
 * logs) are hardcoded for macOS conventions. Cross-platform support would require
 * platform detection and path abstraction.
 */
import { FileSystem, Terminal } from "@effect/platform";
import { Data, Effect, Option } from "effect";
import * as os from "node:os";
// ============================================================================
// Errors
// ============================================================================
export class SetupError extends Data.TaggedError("SetupError") {
}
// ============================================================================
// Constants
// ============================================================================
const DAEMON_LABEL = "com.ghostflow.daemon";
const LOG_DIR = "ghostflow";
// ============================================================================
// Path Helpers
// ============================================================================
/**
 * Get the config directory path for the current platform.
 * macOS: ~/Library/Application Support/ghostflow
 */
const getConfigDir = () => {
    const home = os.homedir();
    return `${home}/Library/Application Support/ghostflow`;
};
/** Get the LaunchAgents directory path. */
const getLaunchAgentsDir = () => {
    const home = os.homedir();
    return `${home}/Library/LaunchAgents`;
};
/** Get the logs directory path. */
const getLogsDir = () => {
    const home = os.homedir();
    return `${home}/Library/Logs/${LOG_DIR}`;
};
/** Get the plist file path. */
const getPlistPath = () => `${getLaunchAgentsDir()}/${DAEMON_LABEL}.plist`;
/** Get the .env file path. */
const getEnvPath = () => `${getConfigDir()}/.env`;
/** Expand ~ to home directory in path. */
const expandTilde = (path) => path.startsWith("~") ? path.replace("~", os.homedir()) : path;
// ============================================================================
// Public API
// ============================================================================
/**
 * Run first-time setup if needed.
 *
 * Returns information about what was set up.
 * If everything is already configured, returns quickly with no changes.
 */
export const ensureSetup = (daemonPath) => Effect.gen(function* () {
    // Step 1: Ensure config directory
    const configCreated = yield* ensureConfigDir();
    // Step 2 & 3: Ensure logs and LaunchAgents directories (parallel)
    yield* ensureRequiredDirs();
    // Step 4: Ensure .env has REPO_PATH
    const envResult = yield* ensureEnvConfigured();
    // Step 5: Ensure plist is valid
    const plistCreated = yield* ensurePlistValid(daemonPath);
    return {
        configCreated,
        envCreated: envResult.created,
        plistCreated,
        repoPath: envResult.path
    };
});
/**
 * Check if setup has been completed (all required files exist).
 */
export const isSetupComplete = (daemonPath) => Effect.gen(function* () {
    const hasRepo = yield* hasRepoPathConfigured();
    const hasPlist = yield* isPlistValid(daemonPath);
    return hasRepo && hasPlist;
});
// ============================================================================
// Directory Management
// ============================================================================
/**
 * Ensure a directory exists, creating it if necessary.
 */
const ensureDir = (dirPath) => Effect.gen(function* () {
    const fs = yield* FileSystem.FileSystem;
    const exists = yield* fs.exists(dirPath);
    yield* Effect.when(fs.makeDirectory(dirPath, { recursive: true }), () => !exists);
});
/** Ensure config directory exists, returning whether it was created. */
const ensureConfigDir = () => Effect.gen(function* () {
    const fs = yield* FileSystem.FileSystem;
    const configDir = getConfigDir();
    const exists = yield* fs.exists(configDir);
    const needsCreation = !exists;
    yield* Effect.when(ensureDir(configDir), () => needsCreation);
    return needsCreation;
});
/** Ensure all required directories exist (logs, LaunchAgents). */
const ensureRequiredDirs = () => Effect.all([ensureDir(getLogsDir()), ensureDir(getLaunchAgentsDir())], { discard: true });
// ============================================================================
// Environment Configuration
// ============================================================================
/** Check if .env file exists and has REPO_PATH configured. */
const hasRepoPathConfigured = () => Effect.gen(function* () {
    const fs = yield* FileSystem.FileSystem;
    const envPath = getEnvPath();
    const exists = yield* fs.exists(envPath);
    return yield* Effect.if(exists, {
        onTrue: () => fs.readFileString(envPath).pipe(Effect.map((content) => content.includes("REPO_PATH="))),
        onFalse: () => Effect.succeed(false)
    });
});
/**
 * Validate that a path is a valid repository path.
 * Checks that the path is non-empty, expands ~, and verifies the directory exists.
 */
const validateRepoPath = (input) => Effect.gen(function* () {
    const trimmed = input.trim();
    const fs = yield* FileSystem.FileSystem;
    yield* Effect.filterOrFail(Effect.succeed(trimmed), (t) => t.length > 0, () => new SetupError({ message: "Repository path cannot be empty" }));
    const expandedPath = expandTilde(trimmed);
    // Validate the directory exists
    const exists = yield* fs.exists(expandedPath);
    yield* Effect.filterOrFail(Effect.succeed(exists), (e) => e === true, () => new SetupError({ message: `Directory does not exist: ${expandedPath}` }));
    // Validate it's a directory
    const stat = yield* fs.stat(expandedPath);
    yield* Effect.filterOrFail(Effect.succeed(stat.type), (t) => t === "Directory", () => new SetupError({ message: `Path is not a directory: ${expandedPath}` }));
    return expandedPath;
});
/** Prompt user for repository path. */
const promptForRepoPath = () => Effect.gen(function* () {
    const terminal = yield* Terminal.Terminal;
    yield* terminal.display("\nGhostflow needs a repository path to sync.\n\n");
    yield* terminal.display("Enter the path to your git repository: ");
    const input = yield* terminal.readLine;
    return yield* validateRepoPath(input);
});
/** Write .env file with REPO_PATH. */
const writeEnvFile = (repoPath) => Effect.gen(function* () {
    const fs = yield* FileSystem.FileSystem;
    const envPath = getEnvPath();
    yield* fs.writeFileString(envPath, `REPO_PATH=${repoPath}\n`);
});
/**
 * Ensure .env file has REPO_PATH configured.
 * Returns the path and whether it was newly created.
 */
const ensureEnvConfigured = () => Effect.gen(function* () {
    const hasRepo = yield* hasRepoPathConfigured();
    return yield* Effect.if(hasRepo, {
        onTrue: () => Effect.succeed({ created: false, path: Option.none() }),
        onFalse: () => Effect.gen(function* () {
            const path = yield* promptForRepoPath();
            yield* writeEnvFile(path);
            return { created: true, path: Option.some(path) };
        })
    });
});
// ============================================================================
// Plist Management
// ============================================================================
/** Generate launchd plist content. */
const generatePlist = (daemonPath) => {
    const configDir = getConfigDir();
    const logsDir = getLogsDir();
    return `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>${DAEMON_LABEL}</string>
  <key>ProgramArguments</key>
  <array>
    <string>${daemonPath}</string>
  </array>
  <key>RunAtLoad</key>
  <false/>
  <key>KeepAlive</key>
  <true/>
  <key>WorkingDirectory</key>
  <string>${configDir}</string>
  <key>StandardOutPath</key>
  <string>${logsDir}/daemon.log</string>
  <key>StandardErrorPath</key>
  <string>${logsDir}/daemon.err</string>
</dict>
</plist>
`;
};
/** Check if plist content is valid (contains daemon path and working directory). */
const isPlistContentValid = (content, daemonPath) => content.includes(daemonPath) && content.includes("<key>WorkingDirectory</key>");
/** Check if plist exists and has correct daemon path. */
const isPlistValid = (daemonPath) => Effect.gen(function* () {
    const fs = yield* FileSystem.FileSystem;
    const plistPath = getPlistPath();
    const exists = yield* fs.exists(plistPath);
    return yield* Effect.if(exists, {
        onTrue: () => fs.readFileString(plistPath).pipe(Effect.map((content) => isPlistContentValid(content, daemonPath))),
        onFalse: () => Effect.succeed(false)
    });
});
/** Write launchd plist file. */
const writePlist = (daemonPath) => Effect.gen(function* () {
    const fs = yield* FileSystem.FileSystem;
    const plistPath = getPlistPath();
    const content = generatePlist(daemonPath);
    yield* fs.writeFileString(plistPath, content);
});
/** Ensure plist is valid, returning whether it was created. */
const ensurePlistValid = (daemonPath) => Effect.gen(function* () {
    const valid = yield* isPlistValid(daemonPath);
    const needsCreation = !valid;
    yield* Effect.when(writePlist(daemonPath), () => needsCreation);
    return needsCreation;
});
//# sourceMappingURL=setup.js.map