const std = @import("std");
const types = @import("types.zig");
const print = std.debug.print;

pub const HELP_TEXT =
    \\Usage: manwhere [OPTIONS] <keyword>
    \\Find man pages containing a keyword.
    \\
    \\OPTIONS:
    \\  -h, --help           Show this help message
    \\  -v, --verbose        Show timing and extra details
    \\      --paths          Show file paths (slower)
    \\  -s, --section NUM    Only show results from section NUM (1-9)
    \\                       Can be specified multiple times
    \\
    \\EXAMPLES:
    \\  manwhere sleep                    # find all man pages mentioning "sleep"
    \\  manwhere -s 1 sleep               # find only commands (section 1) mentioning "sleep"
    \\  manwhere -s 1 -s 3 sleep          # find in sections 1 and 3
    \\  manwhere -v --paths ssl           # detailed search with paths and timing
    \\  manwhere --section 3 printf       # find only library functions (section 3)
    \\
;

pub fn parseArgs(allocator: std.mem.Allocator, args: [][:0]u8) !types.SearchConfig {
    if (args.len < 2) {
        print("{s}", .{HELP_TEXT});
        return error.NoKeyword;
    }

    var keyword: []const u8 = "";
    var sections_list = std.ArrayList([]const u8){};
    defer sections_list.deinit(allocator);
    var show_paths = false;
    var verbose = false;

    var i: usize = 1;
    while (i < args.len) : (i += 1) {
        const arg = args[i];

        if (std.mem.eql(u8, arg, "-h") or std.mem.eql(u8, arg, "--help")) {
            print("{s}", .{HELP_TEXT});
            return error.HelpRequested;
        } else if (std.mem.eql(u8, arg, "--paths")) {
            show_paths = true;
        } else if (std.mem.eql(u8, arg, "-v") or std.mem.eql(u8, arg, "--verbose")) {
            verbose = true;
        } else if (std.mem.eql(u8, arg, "-s") or std.mem.eql(u8, arg, "--section")) {
            // Next argument should be the section number
            if (i + 1 >= args.len) {
                print("[X] Option {s} requires a section number (1-9)\n", .{arg});
                return error.InvalidOption;
            }
            i += 1;
            const section_arg = args[i];

            // Validate section number
            if (section_arg.len == 0 or section_arg.len > 4) {
                print("[X] Invalid section '{s}'. Use 1-9 or subsections like 3ssl\n", .{section_arg});
                return error.InvalidSection;
            }

            // Basic validation - should start with a digit 1-9
            if (section_arg[0] < '1' or section_arg[0] > '9') {
                print("[X] Invalid section '{s}'. Section must be 1-9\n", .{section_arg});
                return error.InvalidSection;
            }

            // Add section to list
            try sections_list.append(allocator, section_arg);
        } else if (std.mem.startsWith(u8, arg, "-")) {
            print("[X] Unknown option: {s}\nUse -h or --help for usage information\n", .{arg});
            return error.InvalidOption;
        } else if (keyword.len == 0) {
            keyword = arg;
        } else {
            print("[X] Multiple keywords not supported\n", .{});
            return error.MultipleKeywords;
        }
    }

    if (keyword.len == 0) {
        print("[X] No keyword provided\n{s}", .{HELP_TEXT});
        return error.NoKeyword;
    }

    // Convert ArrayList to owned slice, or null if empty
    const target_sections = if (sections_list.items.len > 0)
        try sections_list.toOwnedSlice(allocator)
    else
        null;

    return types.SearchConfig{
        .keyword = keyword,
        .target_sections = target_sections,
        .show_paths = show_paths,
        .verbose = verbose,
    };
}

// ============================================================================
// TESTS
// ============================================================================

test "parseArgs - basic keyword" {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const args = &[_][:0]u8{ "manwhere", "sleep" };
    const config = try parseArgs(allocator, args);

    try std.testing.expectEqualStrings("sleep", config.keyword);
    try std.testing.expect(config.target_sections == null);
    try std.testing.expect(!config.show_paths);
    try std.testing.expect(!config.verbose);
}

test "parseArgs - with verbose flag" {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const args = &[_][:0]u8{ "manwhere", "-v", "ls" };
    const config = try parseArgs(allocator, args);

    try std.testing.expectEqualStrings("ls", config.keyword);
    try std.testing.expect(config.verbose);
}

test "parseArgs - with paths flag" {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const args = &[_][:0]u8{ "manwhere", "--paths", "printf" };
    const config = try parseArgs(allocator, args);

    try std.testing.expectEqualStrings("printf", config.keyword);
    try std.testing.expect(config.show_paths);
}

test "parseArgs - with section flag" {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const args = &[_][:0]u8{ "manwhere", "-s", "1", "ls" };
    const config = try parseArgs(allocator, args);

    try std.testing.expectEqualStrings("ls", config.keyword);
    try std.testing.expect(config.target_sections != null);
    try std.testing.expectEqual(@as(usize, 1), config.target_sections.?.len);
    try std.testing.expectEqualStrings("1", config.target_sections.?[0]);
}

test "parseArgs - with multiple sections" {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const args = &[_][:0]u8{ "manwhere", "-s", "1", "-s", "3", "test" };
    const config = try parseArgs(allocator, args);

    try std.testing.expectEqualStrings("test", config.keyword);
    try std.testing.expect(config.target_sections != null);
    try std.testing.expectEqual(@as(usize, 2), config.target_sections.?.len);
    try std.testing.expectEqualStrings("1", config.target_sections.?[0]);
    try std.testing.expectEqualStrings("3", config.target_sections.?[1]);
}

test "parseArgs - error no keyword" {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const args = &[_][:0]u8{"manwhere"};
    const result = parseArgs(allocator, args);

    try std.testing.expectError(error.NoKeyword, result);
}

test "parseArgs - error invalid option" {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const args = &[_][:0]u8{ "manwhere", "--invalid", "ls" };
    const result = parseArgs(allocator, args);

    try std.testing.expectError(error.InvalidOption, result);
}

test "parseArgs - error multiple keywords" {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const args = &[_][:0]u8{ "manwhere", "ls", "cat" };
    const result = parseArgs(allocator, args);

    try std.testing.expectError(error.MultipleKeywords, result);
}
