const std = @import("std");
const config = @import("../config.zig");
const output = @import("output.zig");

/// Execution mode for commands
pub const Mode = enum {
    local,
    server,
};

/// Execution context passed to all command handlers
/// Provides unified access to allocator, config, and output mode
pub const Context = struct {
    allocator: std.mem.Allocator,
    mode: Mode,
    cfg: config.Config,
    json_output: bool,

    /// Initialize context from config
    pub fn init(allocator: std.mem.Allocator, cfg: config.Config, json_output: bool) Context {
        const mode: Mode = if (cfg.isLocalMode()) .local else .server;
        return .{
            .allocator = allocator,
            .mode = mode,
            .cfg = cfg,
            .json_output = json_output,
        };
    }

    /// Clean up context resources
    pub fn deinit(self: *Context) void {
        self.cfg.deinit(self.allocator);
    }

    /// Check if running in local mode
    pub fn isLocal(self: Context) bool {
        return self.mode == .local;
    }

    /// Check if running in server mode
    pub fn isServer(self: Context) bool {
        return self.mode == .server;
    }

    /// Dispatch to appropriate implementation based on mode
    /// local_fn: function to call in local mode
    /// server_fn: function to call in server mode
    /// Both functions must have the same signature: fn (Context, []const []const u8) anyerror!void
    pub fn dispatch(
        self: Context,
        local_fn: *const fn (Context, []const []const u8) anyerror!void,
        server_fn: *const fn (Context, []const []const u8) anyerror!void,
        args: []const []const u8,
    ) !void {
        switch (self.mode) {
            .local => return local_fn(self, args),
            .server => return server_fn(self, args),
        }
    }

    /// Dispatch with result - returns a value
    pub fn dispatchWithResult(
        self: Context,
        local_fn: *const fn (Context, []const []const u8) anyerror![]const u8,
        server_fn: *const fn (Context, []const []const u8) anyerror![]const u8,
        args: []const []const u8,
    ) ![]const u8 {
        switch (self.mode) {
            .local => return local_fn(self, args),
            .server => return server_fn(self, args),
        }
    }

    /// Output helpers that respect context settings
    pub fn errorMsg(self: Context, comptime cmd: []const u8, message: []const u8) void {
        if (self.json_output) {
            output.errorMsg(cmd, message);
        } else {
            std.log.err("{s}: {s}", .{ cmd, message });
        }
    }

    pub fn errorMsgDetailed(self: Context, comptime cmd: []const u8, message: []const u8, details: []const u8) void {
        if (self.json_output) {
            output.errorMsgDetailed(cmd, message, details);
        } else {
            std.log.err("{s}: {s} - {s}", .{ cmd, message, details });
        }
    }

    pub fn success(self: Context, comptime cmd: []const u8) void {
        if (self.json_output) {
            output.success(cmd);
        }
    }

    pub fn successString(self: Context, comptime cmd: []const u8, comptime key: []const u8, value: []const u8) void {
        if (self.json_output) {
            output.successString(cmd, key, value);
        } else {
            std.debug.print("{s}: {s}\n", .{ key, value });
        }
    }

    pub fn info(self: Context, comptime fmt: []const u8, args: anytype) void {
        if (!self.json_output) {
            std.debug.print(fmt ++ "\n", args);
        }
    }

    pub fn printUsage(_: Context, comptime cmd: []const u8, comptime usage: []const u8) void {
        output.usage(cmd, usage);
    }
};

/// Require subcommand helper
pub fn requireSubcommand(args: []const []const u8, comptime cmd_name: []const u8) ![]const u8 {
    if (args.len == 0) {
        std.log.err("Command '{s}' requires a subcommand", .{cmd_name});
        return error.MissingSubcommand;
    }
    return args[0];
}

/// Match subcommand and return remaining args
pub fn matchSubcommand(args: []const []const u8, comptime sub: []const u8) ?[]const []const u8 {
    if (args.len == 0) return null;
    if (std.mem.eql(u8, args[0], sub)) {
        return args[1..];
    }
    return null;
}
