const std = @import("std");

/// Common flags supported by most commands
pub const CommonFlags = struct {
    json: bool = false,
    help: bool = false,
    verbose: bool = false,
    dry_run: bool = false,
    color: ?bool = null, // null = auto, true = force on, false = disable
};

/// Parse common flags from command arguments
/// Returns remaining non-flag arguments
pub fn parseCommon(allocator: std.mem.Allocator, args: []const []const u8, flags: *CommonFlags) !std.ArrayList([]const u8) {
    var remaining = std.ArrayList([]const u8).initCapacity(allocator, args.len) catch |err| {
        return err;
    };
    errdefer remaining.deinit(allocator);

    var i: usize = 0;
    while (i < args.len) : (i += 1) {
        const arg = args[i];
        if (std.mem.eql(u8, arg, "--json")) {
            flags.json = true;
        } else if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) {
            flags.help = true;
        } else if (std.mem.eql(u8, arg, "--verbose") or std.mem.eql(u8, arg, "-v")) {
            flags.verbose = true;
        } else if (std.mem.eql(u8, arg, "--dry-run")) {
            flags.dry_run = true;
        } else if (std.mem.eql(u8, arg, "--no-color") or std.mem.eql(u8, arg, "--no-colour")) {
            flags.color = false;
        } else if (std.mem.startsWith(u8, arg, "--color=")) {
            const val = arg[8..];
            if (std.mem.eql(u8, val, "always") or std.mem.eql(u8, val, "yes")) {
                flags.color = true;
            } else if (std.mem.eql(u8, val, "never") or std.mem.eql(u8, val, "no")) {
                flags.color = false;
            } else if (std.mem.eql(u8, val, "auto")) {
                flags.color = null;
            }
        } else if (std.mem.eql(u8, arg, "--")) {
            // End of flags, rest are positional
            i += 1;
            while (i < args.len) : (i += 1) {
                try remaining.append(allocator, args[i]);
            }
            break;
        } else {
            try remaining.append(allocator, arg);
        }
    }

    return remaining;
}

/// Parse a key-value flag (--key=value or --key value)
pub fn parseKVFlag(args: []const []const u8, key: []const u8) ?[]const u8 {
    const prefix = std.fmt.allocPrint(std.heap.page_allocator, "--{s}=", .{key}) catch return null;
    defer std.heap.page_allocator.free(prefix);

    for (args) |arg| {
        if (std.mem.startsWith(u8, arg, prefix)) {
            return arg[prefix.len..];
        }
    }

    // Check for --key value format
    var i: usize = 0;
    const key_only = std.fmt.allocPrint(std.heap.page_allocator, "--{s}", .{key}) catch return null;
    defer std.heap.page_allocator.free(key_only);

    while (i < args.len) : (i += 1) {
        if (std.mem.eql(u8, args[i], key_only)) {
            if (i + 1 < args.len) {
                return args[i + 1];
            }
            return null;
        }
    }

    return null;
}

/// Parse a boolean flag
pub fn parseBoolFlag(args: []const []const u8, flag: []const u8) bool {
    const full_flag = std.fmt.allocPrint(std.heap.page_allocator, "--{s}", .{flag}) catch return false;
    defer std.heap.page_allocator.free(full_flag);

    for (args) |arg| {
        if (std.mem.eql(u8, arg, full_flag)) {
            return true;
        }
    }
    return false;
}

/// Parse numeric flag with default value
pub fn parseNumFlag(comptime T: type, args: []const []const u8, flag: []const u8, default: T) T {
    const val_str = parseKVFlag(args, flag);
    if (val_str) |s| {
        return std.fmt.parseInt(T, s, 10) catch default;
    }
    return default;
}

/// Check if args contain any of the given flags
pub fn hasAnyFlag(args: []const []const u8, flags: []const []const u8) bool {
    for (args) |arg| {
        for (flags) |flag| {
            if (std.mem.eql(u8, arg, flag)) {
                return true;
            }
        }
    }
    return false;
}

/// Shift/pop first argument
pub fn shift(args: []const []const u8) ?[]const u8 {
    if (args.len == 0) return null;
    return args[0];
}

/// Get remaining arguments after first
pub fn rest(args: []const []const u8) []const []const u8 {
    if (args.len <= 1) return &[]const u8{};
    return args[1..];
}

/// Require subcommand, return error if missing
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;
}
