/**
 * Add Command
 *
 * Adds a repository to Ghostflow for automatic synchronization.
 * Works without the daemon running by writing to config directly.
 * If the daemon is running, it will be notified to reload the configuration.
 */
import { Args, Command, Options } from "@effect/cli";
import { FileSystem, Path } from "@effect/platform";
import { ConfigManager, GhostflowClient, isConfigStorageError, isFlowError, reloadWithVerification } from "@ghostflow/client";
import { decodeAbsolutePath } from "@ghostflow/client/schemas";
import { Console, Data, Effect, Match, Option as O, pipe } from "effect";
import { ANSI, msg } from "../constants/ansi.js";
// ============================================================================
// Errors
// ============================================================================
class AddFlowError extends Data.TaggedError("AddFlowError") {
}
// ============================================================================
// Arguments & Options
// ============================================================================
const pathArg = Args.directory({ name: "path" }).pipe(Args.withDescription("Path to the git repository to add"));
const nameOption = Options.text("name").pipe(Options.withAlias("n"), Options.withDescription("Custom name for the flow (defaults to directory name)"), Options.optional);
const remoteOption = Options.text("remote").pipe(Options.withAlias("r"), Options.withDescription("Git remote to sync with (defaults to 'origin')"), Options.withDefault("origin"));
const branchOption = Options.text("branch").pipe(Options.withAlias("b"), Options.withDescription("Branch to sync (defaults to current branch)"), Options.optional);
// ============================================================================
// Git Validation Helpers
// ============================================================================
/**
 * Check if a path is a git repository.
 */
const isGitRepository = (repoPath) => Effect.gen(function* () {
    const fs = yield* FileSystem.FileSystem;
    const path = yield* Path.Path;
    const gitDir = path.join(repoPath, ".git");
    return yield* fs.exists(gitDir);
});
/**
 * Get the absolute path of a directory.
 */
const getAbsolutePath = (inputPath) => Effect.gen(function* () {
    const path = yield* Path.Path;
    const cwd = yield* Effect.sync(() => process.cwd());
    // If already absolute, return as-is
    if (path.isAbsolute(inputPath)) {
        return path.normalize(inputPath);
    }
    // Make relative paths absolute
    return path.resolve(cwd, inputPath);
});
/**
 * Validate the repository path and return normalized absolute path as branded type.
 * Uses the AbsolutePath decoder to ensure the path matches the branded type.
 */
const validateRepoPath = (inputPath) => Effect.gen(function* () {
    const fs = yield* FileSystem.FileSystem;
    const absPath = yield* getAbsolutePath(inputPath);
    // Validate against AbsolutePath schema (ensures absolute path, no null bytes)
    const validatedPath = yield* pipe(decodeAbsolutePath(absPath), Effect.mapError(() => new AddFlowError({ message: `Invalid path format: ${absPath}` })));
    // Check path exists
    const exists = yield* pipe(fs.exists(validatedPath), Effect.mapError((err) => new AddFlowError({ message: `Cannot check path: ${validatedPath}: ${err.message}` })));
    if (!exists) {
        return yield* Effect.fail(new AddFlowError({ message: `Path does not exist: ${validatedPath}` }));
    }
    // Check it's a directory
    const stat = yield* pipe(fs.stat(validatedPath), Effect.mapError((err) => new AddFlowError({ message: `Cannot stat path: ${validatedPath}: ${err.message}` })));
    if (stat.type !== "Directory") {
        return yield* Effect.fail(new AddFlowError({ message: `Not a directory: ${validatedPath}` }));
    }
    // Check it's a git repository
    const isGit = yield* pipe(isGitRepository(validatedPath), Effect.mapError((err) => new AddFlowError({ message: `Cannot check git repo: ${validatedPath}: ${err.message}` })));
    if (!isGit) {
        return yield* Effect.fail(new AddFlowError({ message: `Not a git repository: ${validatedPath}\nInitialize with: git init` }));
    }
    return validatedPath;
});
/**
 * Get the directory name from a path for default naming.
 */
const getDirName = (repoPath) => Effect.gen(function* () {
    const path = yield* Path.Path;
    return path.basename(repoPath);
});
// ============================================================================
// Daemon Reload Helper
// ============================================================================
/**
 * Try to reload the daemon if it's running with verification.
 * Returns verification result or undefined if daemon isn't running.
 */
const tryReloadDaemon = () => pipe(Effect.gen(function* () {
    const client = yield* GhostflowClient;
    // Reload with retry but without hash verification
    // (we don't have the expected hash readily available here)
    return yield* reloadWithVerification(client);
}), Effect.catchAll(() => Effect.succeed(undefined)));
// ============================================================================
// Command Implementation
// ============================================================================
const addHandler = ({ branch, name, path: inputPath, remote }) => pipe(Effect.gen(function* () {
    // Validate and normalize the path
    const repoPath = yield* validateRepoPath(inputPath);
    // Determine the flow name
    const flowName = O.isSome(name) ? name.value : yield* getDirName(repoPath);
    const branchName = O.getOrUndefined(branch);
    yield* Console.log(`Adding ${flowName}...`);
    yield* Console.log(`  Path: ${repoPath}`);
    yield* Console.log(`  Remote: ${remote}`);
    if (branchName != null) {
        yield* Console.log(`  Branch: ${branchName}`);
    }
    // Add flow via ConfigManager (writes to config directly)
    const configManager = yield* ConfigManager;
    // Build input object conditionally to satisfy exactOptionalPropertyTypes
    const baseInput = { path: repoPath, remote };
    const withName = { ...baseInput, name: flowName };
    const input = branchName != null ? { ...withName, branch: branchName } : withName;
    const result = yield* configManager.addFlow(input);
    yield* Console.log("");
    yield* Console.log(msg.success(`Added ${flowName} to Ghostflow`));
    yield* Console.log(`  Flow ID: ${result.flow.id}`);
    yield* Console.log(`  Watching: ${repoPath}`);
    // Try to reload daemon with verification (graceful if not running)
    const reloadResult = yield* tryReloadDaemon();
    if (reloadResult !== undefined) {
        const { reloadData } = reloadResult;
        yield* Console.log(`  ${msg.dim(`Daemon reloaded (${reloadData.flowCount} flows active)`)}`);
        if (reloadData.addedFlows.length > 0) {
            yield* Console.log(`  ${msg.dim(`Added flows: ${reloadData.addedFlows.join(", ")}`)}`);
        }
    }
    else {
        yield* Console.log(`  ${ANSI.YELLOW}Note: Start the daemon to begin syncing: ghostflow start${ANSI.RESET}`);
    }
}), Effect.catchAll((error) => Effect.gen(function* () {
    // Handle config storage errors
    if (isConfigStorageError(error)) {
        yield* Console.error(msg.error(`Failed to write configuration: ${error.message}`));
        return yield* Effect.fail(error);
    }
    // Handle flow-specific errors with helpful messages
    if (isFlowError(error)) {
        const message = pipe(Match.value(error.cause), Match.tag("PathAlreadyTracked", ({ path }) => `Path is already being tracked: ${path}\nUse 'ghostflow list' to see existing flows.`), Match.tag("PathNotAccessible", ({ path, reason }) => `Cannot access path: ${path}\nReason: ${reason}`), Match.tag("InvalidFlowConfig", ({ details }) => `Invalid configuration: ${details}`), Match.tag("FlowLimitExceeded", ({ current, limit }) => `Flow limit exceeded (${current}/${limit}).\nUpgrade to add more repositories.`), Match.tag("NestedPathConflict", ({ path }) => `Path conflicts with an existing flow: ${path}\nNested repositories are not supported.`), Match.orElse(() => error.message));
        yield* Console.error(msg.error(message));
        return yield* Effect.fail(error);
    }
    // Handle AddFlowError from validation - user-friendly, no stack trace
    if (error instanceof AddFlowError) {
        yield* Console.error(msg.error(error.message));
        return yield* Effect.void;
    }
    // Generic error
    yield* Console.error(msg.error(String(error)));
    return yield* Effect.fail(error);
})));
// ============================================================================
// Command Definition
// ============================================================================
export const addCommand = Command.make("add", {
    path: pathArg,
    name: nameOption,
    remote: remoteOption,
    branch: branchOption
}).pipe(Command.withHandler(addHandler), Command.withDescription("Add a repository to sync"));
//# sourceMappingURL=add.js.map