require "spec"
require "option_parser"

private def expect_capture_option(args, option, value)
  flag = nil
  OptionParser.parse(args) do |opts|
    opts.on(option, "some flag") do |flag_value|
      flag = flag_value
    end
  end
  flag.should eq(value)
  args.size.should eq(0)
end

private def expect_doesnt_capture_option(args, option)
  flag = false
  OptionParser.parse(args) do |opts|
    opts.on(option, "some flag") do
      flag = true
    end
  end
  flag.should be_false
end

private def expect_missing_option(option)
  expect_raises OptionParser::MissingOption do
    OptionParser.parse([] of String) do |opts|
      opts.on(option, "some flag") do |flag_value|
      end
    end
  end
end

private def expect_missing_option(args, option, flag)
  expect_raises OptionParser::MissingOption, "Missing option: #{flag}" do
    OptionParser.parse(args) do |opts|
      opts.on(option, "some flag") do |flag_value|
      end
    end
  end
end

describe "OptionParser" do
  it "has flag" do
    expect_capture_option ["-f"], "-f", ""
  end

  it "has flag with many letters" do
    expect_capture_option ["-ll"], "-ll", "l"
  end

  it "doesn't have flag" do
    expect_doesnt_capture_option [] of String, "-f"
  end

  it "has flag with double dash" do
    expect_capture_option ["--flag"], "--flag", ""
  end

  it "doesn't have flag with double dash" do
    expect_doesnt_capture_option [] of String, "--flag"
  end

  it "has required option next to flag" do
    expect_capture_option ["-f123"], "-fFLAG", "123"
  end

  it "has required option next to flag but given separated" do
    expect_capture_option ["-f", "123"], "-fFLAG", "123"
  end

  it "raises if missing option next to flag" do
    expect_missing_option ["-f"], "-fFLAG", "-f"
  end

  it "has required option separated from flag" do
    expect_capture_option ["-f", "123"], "-f FLAG", "123"
  end

  it "has required option separated from flag but given together" do
    expect_capture_option ["-f123"], "-f FLAG", "123"
  end

  it "gets short option with value that looks like flag" do
    expect_capture_option ["-f", "-g -h"], "-f FLAG", "-g -h"
  end

  describe "Consumption of flags following an ungiven optional argument" do
    context "Given a short option with an optional value" do
      it "doesn't eat a following short option" do
        flag = nil
        not_eaten = [] of String
        args = ["-f", "-g", "-i"]
        OptionParser.parse(args) do |opts|
          opts.on("-f [FLAG]", "some flag") do |flag_value|
            flag = flag_value
          end
          opts.on("-g", "shouldn't be eaten") do
            not_eaten << "-g"
          end
          opts.on("-i", "shouldn't be eaten") do
            not_eaten << "-i"
          end
        end
        flag.should eq("")
        not_eaten.should eq(["-g", "-i"])
        args.size.should eq(0)
      end

      it "doesn't eat a following long option" do
        flag = nil
        not_eaten = [] of String
        args = ["-f", "--g-long", "-i"]
        OptionParser.parse(args) do |opts|
          opts.on("-f [FLAG]", "some flag") do |flag_value|
            flag = flag_value
          end
          opts.on("-g", "--g-long", "shouldn't be eaten") do
            not_eaten << "--g-long"
          end
          opts.on("-i", "shouldn't be eaten") do
            not_eaten << "-i"
          end
        end
        flag.should eq("")
        not_eaten.should eq(["--g-long", "-i"])
        args.size.should eq(0)
      end

      it "does eat a value that looks like an option" do
        flag = nil
        not_eaten = [] of String
        args = ["-f", "--not-an-option--", "-i"]
        OptionParser.parse(args) do |opts|
          opts.on("-f [FLAG]", "some flag") do |flag_value|
            flag = flag_value
          end
          opts.on("-i", "shouldn't be eaten") do
            not_eaten << "-i"
          end
        end
        flag.should eq("--not-an-option--")
        not_eaten.should eq(["-i"])
        args.size.should eq(0)
      end
    end

    context "Given a long option with an optional value" do
      it "doesn't eat further short options" do
        flag = nil
        not_eaten = [] of String
        args = ["--long-flag", "-g", "-i"]
        OptionParser.parse(args) do |opts|
          opts.on("--long-flag [FLAG]", "some flag") do |flag_value|
            flag = flag_value
          end
          opts.on("-g", "shouldn't be eaten") do
            not_eaten << "-g"
          end
          opts.on("-i", "shouldn't be eaten") do
            not_eaten << "-i"
          end
        end
        flag.should eq("")
        not_eaten.should eq(["-g", "-i"])
        args.size.should eq(0)
      end

      it "doesn't eat further long options" do
        flag = nil
        not_eaten = [] of String
        args = ["--long-flag", "--g-long", "-i"]
        OptionParser.parse(args) do |opts|
          opts.on("--long-flag [FLAG]", "some flag") do |flag_value|
            flag = flag_value
          end
          opts.on("--g-long", "shouldn't be eaten") do
            not_eaten << "--g-long"
          end
          opts.on("-i", "shouldn't be eaten") do
            not_eaten << "-i"
          end
        end
        flag.should eq("")
        not_eaten.should eq(["--g-long", "-i"])
        args.size.should eq(0)
      end
    end
  end

  it "raises if missing required option with space" do
    expect_missing_option ["-f"], "-f FLAG", "-f"
  end

  it "has required option separated from long flag" do
    expect_capture_option ["--flag", "123"], "--flag FLAG", "123"
  end

  it "has required option with =" do
    expect_capture_option ["--flag=123"], "--flag FLAG", "123"
  end

  it "has required option with = (2)" do
    expect_capture_option ["--flag=123"], "--flag=FLAG", "123"
  end

  it "has required option with = (3) handles empty" do
    expect_capture_option ["--flag="], "--flag=FLAG", ""
  end

  it "raises if missing required argument separated from long flag" do
    expect_missing_option ["--flag"], "--flag FLAG", "--flag"
  end

  it "has required option with space" do
    expect_capture_option ["-f", "123"], "-f ", "123"
  end

  it "has required option with long flag space" do
    expect_capture_option ["--flag", "123"], "--flag ", "123"
  end

  it "gets short option with value -- (#8937)" do
    expect_capture_option ["-f", "--"], "-f ARG", "--"
  end

  it "gets long option with value -- (#8937)" do
    expect_capture_option ["--flag", "--"], "--flag [ARG]", "--"
  end

  it "doesn't raise if required option is not specified" do
    expect_doesnt_capture_option [] of String, "-f "
  end

  it "doesn't raise if optional option is not specified with short flag" do
    expect_doesnt_capture_option [] of String, "-f[FLAG]"
  end

  it "doesn't raise if optional option is not specified with long flag" do
    expect_doesnt_capture_option [] of String, "--flag [FLAG]"
  end

  it "doesn't raise if optional option is not specified with separated short flag" do
    expect_doesnt_capture_option [] of String, "-f [FLAG]"
  end

  it "doesn't raise if required option is not specified with separated short flag" do
    expect_doesnt_capture_option [] of String, "-f FLAG"
  end

  describe "bundling" do
    it "parses bundled boolean short options" do
      args = %w(-rf)
      called = [] of String
      OptionParser.parse(args) do |opts|
        opts.on("-r", "") { called << "-r" }
        opts.on("-f", "") { called << "-f" }
      end
      called.should eq(%w(-r -f))
      args.size.should eq(0)
    end

    it "re-triggers handlers for repeated short flags" do
      args = %w(-vvv)
      verbosity = 0
      OptionParser.parse(args) do |opts|
        opts.on("-v", "") { verbosity += 1 }
      end
      verbosity.should eq(3)
      args.size.should eq(0)
    end

    it "uses rest of bundle as value for required option" do
      value = nil
      a = false
      parser = OptionParser.new do |opts|
        opts.on("-a", "") { a = true }
        opts.on("-o VALUE", "") { |v| value = v }
      end

      args = %w(-ovalue -a)
      parser.parse(args)
      value.should eq("value")
      a.should be_true
      args.size.should eq(0)

      value = nil
      a = false
      args = %w(-ao123)
      parser.parse(args)
      a.should be_true
      value.should eq("123")
      args.size.should eq(0)
    end

    it "does not bundle when some options are unknown" do
      n = false
      parser = OptionParser.new do |opts|
        opts.on("-n", "") { n = true }
      end

      expect_raises OptionParser::InvalidOption, "Invalid option: -nc" do
        parser.parse(["-nc"])
      end
      n.should be_false

      args = %w(-nc)
      parser.invalid_option { }
      parser.parse(args)
      n.should be_false
      args.should eq(%w(-nc))
    end

    it "consumes rest of bundle as argument value when middle option requires argument" do
      args = %w(-aeb)
      a = false
      b = false
      e = nil
      OptionParser.parse(args) do |opts|
        opts.on("-a", "") { a = true }
        opts.on("-b", "") { b = true }
        opts.on("-e VALUE", "") { |v| e = v }
      end
      a.should be_true
      b.should be_false
      e.should eq("b")
      args.size.should eq(0)
    end
  end

  describe "gnu_optional_args" do
    it "doesn't get optional argument for short flag after space" do
      flag = nil
      args = %w(-f 123)
      OptionParser.parse(args, gnu_optional_args: true) do |opts|
        opts.on("-f [FLAG]", "some flag") do |flag_value|
          flag = flag_value
        end
      end
      flag.should eq("")
      args.should eq(%w(123))
    end

    it "doesn't get optional argument for long flag after space" do
      flag = nil
      args = %w(--f 123)
      OptionParser.parse(args, gnu_optional_args: true) do |opts|
        opts.on("--f [FLAG]", "some flag") do |flag_value|
          flag = flag_value
        end
      end
      flag.should eq("")
      args.should eq(%w(123))
    end
  end

  it "parses argument when only referenced in long flag" do
    captured = ""
    parser = OptionParser.parse([] of String) do |opts|
      opts.on("-f", "--flag X", "some flag") { |x| captured = x }
    end
    parser.parse(["-f", "12"])
    captured.should eq "12"
    parser.to_s.should contain "   -f, --flag X"
  end

  it "parses argument when referenced in long and short flag" do
    captured = ""
    parser = OptionParser.parse([] of String) do |opts|
      opts.on("-f X", "--flag X", "some flag") { |x| captured = x }
    end
    parser.parse(["-f", "12"])
    captured.should eq "12"
    parser.to_s.should contain "   -f X, --flag X"
  end

  it "does to_s with banner" do
    parser = OptionParser.parse([] of String) do |opts|
      opts.banner = "Usage: foo"
      opts.on("-f", "--flag", "some flag") do
      end
      opts.on("-g[FLAG]", "some other flag") do
      end
    end
    parser.to_s.should eq <<-USAGE
      Usage: foo
          -f, --flag                       some flag
          -g[FLAG]                         some other flag
      USAGE
  end

  it "does to_s with separators" do
    parser = OptionParser.parse([] of String) do |opts|
      opts.banner = "Usage: foo"
      opts.separator
      opts.separator "Type F flags:"
      opts.on("-f", "--flag", "some flag") do
      end
      opts.separator
      opts.separator "Type G flags:"
      opts.on("-g[FLAG]", "some other flag") do
      end
    end
    parser.to_s.should eq <<-USAGE
      Usage: foo

      Type F flags:
          -f, --flag                       some flag

      Type G flags:
          -g[FLAG]                         some other flag
      USAGE
  end

  it "does to_s with very long flag (#3305)" do
    parser = OptionParser.parse([] of String) do |opts|
      opts.banner = "Usage: foo"
      opts.on("--very_long_option_kills=formatter", "long") do
      end
      opts.on("-f", "--flag", "some flag") do
      end
      opts.on("-g[FLAG]", "some other flag") do
      end
    end
    parser.to_s.should eq <<-USAGE
      Usage: foo
              --very_long_option_kills=formatter
                                           long
          -f, --flag                       some flag
          -g[FLAG]                         some other flag
      USAGE
  end

  it "does to_s with multi line description (#5832)" do
    parser = OptionParser.parse([] of String) do |opts|
      opts.banner = "Usage: foo"
      opts.on("--very_long_option_kills=formatter", "long flag with\nmultiline description") do
      end
      opts.on("-f", "--flag", "some flag with\nmultiline description") do
      end
      opts.on("-g[FLAG]", "some other flag") do
      end
    end
    parser.to_s.should eq <<-USAGE
      Usage: foo
              --very_long_option_kills=formatter
                                           long flag with
                                           multiline description
          -f, --flag                       some flag with
                                           multiline description
          -g[FLAG]                         some other flag
      USAGE
  end

  it "raises on invalid option" do
    expect_raises OptionParser::InvalidOption, "Invalid option: -j" do
      OptionParser.parse(["-f", "-j"]) do |opts|
        opts.on("-f", "some flag") { }
      end
    end
  end

  it "raises on invalid option if value is given to none value handler (short flag, #9553) " do
    expect_raises OptionParser::InvalidOption, "Invalid option: -foo" do
      OptionParser.parse(["-foo"]) do |opts|
        opts.on("-f", "some flag") { }
      end
    end
  end

  it "raises on invalid option if value is given to none value handler (long flag, #9553)" do
    expect_raises OptionParser::InvalidOption, "Invalid option: --foo=bar" do
      OptionParser.parse(["--foo=bar"]) do |opts|
        opts.on("-foo", "some flag") { }
      end
    end
  end

  it "calls the handler for invalid options" do
    called = false
    OptionParser.parse(["-f", "-j"]) do |opts|
      opts.on("-f", "some flag") { }
      opts.invalid_option do |flag|
        flag.should eq("-j")
        called = true
      end
    end

    called.should be_true
  end

  it "calls the handler for missing options" do
    called = false
    OptionParser.parse(["-f"]) do |opts|
      opts.on("-f FOO", "some flag") { }
      opts.missing_option do |flag|
        flag.should eq("-f")
        called = true
      end
    end

    called.should be_true
  end

  describe "multiple times" do
    it "gets an existence flag multiple times" do
      args = %w(-f -f -f)
      count = 0
      OptionParser.parse(args) do |opts|
        opts.on("-f", "some flag") do
          count += 1
        end
      end
      count.should eq(3)
    end

    it "gets a single flag option multiple times" do
      args = %w(-f 1 -f 2)
      values = [] of String
      OptionParser.parse(args) do |opts|
        opts.on("-f VALUE", "some flag") do |value|
          values << value
        end
      end
      values.should eq(%w(1 2))
    end

    it "gets a double flag option multiple times" do
      args = %w(--f 1 --f 2)
      values = [] of String
      OptionParser.parse(args) do |opts|
        opts.on("--f VALUE", "some flag") do |value|
          values << value
        end
      end
      values.should eq(%w(1 2))
    end
  end

  describe "--" do
    it "ignores everything after -- with bool flag" do
      args = ["-f", "bar", "--", "baz", "qux", "-g"]
      f = false
      g = false
      OptionParser.parse(args) do |opts|
        opts.on("-f", "some flag") do
          f = true
        end
        opts.on("-g", "some flag") do
          g = true
        end
      end
      f.should be_true
      g.should be_false
      args.should eq(["bar", "baz", "qux", "-g"])
    end

    it "ignores everything after -- with single flag)" do
      args = ["-f", "bar", "x", "--", "baz", "qux", "-g", "lala"]
      f = nil
      g = nil
      OptionParser.parse(args) do |opts|
        opts.on("-f FLAG", "some flag") do |v|
          f = v
        end
        opts.on("-g FLAG", "some flag") do |v|
          g = v
        end
      end
      f.should eq("bar")
      g.should be_nil
      args.should eq(["x", "baz", "qux", "-g", "lala"])
    end

    it "ignores everything after -- with double flag" do
      args = ["--f", "bar", "x", "--", "baz", "qux", "--g", "lala"]
      f = nil
      g = nil
      OptionParser.parse(args) do |opts|
        opts.on("--f FLAG", "some flag") do |v|
          f = v
        end
        opts.on("--g FLAG", "some flag") do |v|
          g = v
        end
      end
      f.should eq("bar")
      g.should be_nil
      args.should eq(["x", "baz", "qux", "--g", "lala"])
    end

    it "returns a pair with things coming before and after --" do
      args = %w(--f bar baz -- qux)
      f = nil
      unknown_args = nil
      OptionParser.parse(args) do |opts|
        opts.on("--f FLAG", "some flag") do |v|
          f = v
        end
        opts.unknown_args do |before_dash, after_dash|
          unknown_args = {before_dash, after_dash}
        end
      end
      f.should eq("bar")
      args.should eq(["baz", "qux"])
      unknown_args.should eq({["baz"], ["qux"]})
    end

    it "returns a pair with things coming before and after --, without --" do
      args = %w(--f bar baz)
      f = nil
      unknown_args = nil
      OptionParser.parse(args) do |opts|
        opts.on("--f FLAG", "some flag") do |v|
          f = v
        end
        opts.unknown_args do |before_dash, after_dash|
          unknown_args = {before_dash, after_dash}
        end
      end
      f.should eq("bar")
      args.should eq(["baz"])
      unknown_args.should eq({["baz"], [] of String})
    end

    it "initializes without block and does parse!" do
      old_argv = ARGV.dup
      begin
        ARGV.clear
        ARGV.concat %w(--f hi)
        f = nil
        OptionParser.new do |opts|
          opts.on("--f FLAG", "some flag") do |v|
            f = v
          end
        end.parse
        f.should eq("hi")
      ensure
        ARGV.clear
        ARGV.concat old_argv
      end
    end

    it "gets `-` as argument" do
      args = %w(-)
      OptionParser.parse(args) do |opts|
      end
      args.should eq(%w(-))
    end
  end

  describe "forward-match" do
    it "distinguishes between '--lamb VALUE' and '--lambda VALUE'" do
      args = %w(--lamb value1 --lambda value2)
      value1 = nil
      value2 = nil
      OptionParser.parse(args) do |opts|
        opts.on("--lamb VALUE", "") { |v| value1 = v }
        opts.on("--lambda VALUE", "") { |v| value2 = v }
      end
      value1.should eq("value1")
      value2.should eq("value2")
    end

    it "distinguishes between '--lamb=VALUE' and '--lambda=VALUE'" do
      args = %w(--lamb=value1 --lambda=value2)
      value1 = nil
      value2 = nil
      OptionParser.parse(args) do |opts|
        opts.on("--lamb=VALUE", "") { |v| value1 = v }
        opts.on("--lambda=VALUE", "") { |v| value2 = v }
      end
      value1.should eq("value1")
      value2.should eq("value2")
    end
  end

  it "raises if flag pair doesn't start with dash (#4001)" do
    OptionParser.parse([] of String) do |opts|
      expect_raises ArgumentError, %(Argument 'short_flag' ("foo") must start with a dash) do
        opts.on("foo", "bar", "baz") { }
      end

      expect_raises ArgumentError, %(Argument 'long_flag' ("bar") must start with a dash) do
        opts.on("-foo", "bar", "baz") { }
      end

      opts.on("", "-bar", "baz") { }
    end
  end

  it "handles subcommands" do
    args = %w(--verbose subcommand --foo 1 --bar sub2 -z)
    verbose = false
    subcommand = false
    foo = nil
    bar = false
    sub2 = false
    z = false
    OptionParser.parse(args) do |opts|
      opts.on("subcommand", "") do
        subcommand = true
        opts.on("--foo arg", "") { |v| foo = v }
        opts.on("--bar", "") { bar = true }
        opts.on("sub2", "") { sub2 = true }
      end
      opts.on("--verbose", "") { verbose = true }
      opts.on("-z", "--baz", "") { z = true }
    end

    verbose.should be_true
    subcommand.should be_true
    foo.should be("1")
    bar.should be_true
    sub2.should be_true
    z.should be_true
  end

  it "parses with subcommands twice" do
    args = %w(--verbose subcommand --foo 1 --bar sub2 -z)
    verbose = false
    subcommand = false
    foo = nil
    bar = false
    sub2 = false
    z = false

    parser = OptionParser.new do |opts|
      opts.on("subcommand", "") do
        subcommand = true
        opts.on("--foo arg", "") { |v| foo = v }
        opts.on("--bar", "") { bar = true }
        opts.on("sub2", "") { sub2 = true }
      end
      opts.on("--verbose", "") { verbose = true }
      opts.on("-z", "--baz", "") { z = true }
    end

    parser.parse args

    verbose.should be_true
    subcommand.should be_true
    foo.should be("1")
    bar.should be_true
    sub2.should be_true
    z.should be_true

    args = %w(--verbose subcommand --foo 1 --bar sub2 -z)
    verbose = false
    subcommand = false
    foo = nil
    bar = false
    sub2 = false
    z = false

    parser.parse args

    verbose.should be_true
    subcommand.should be_true
    foo.should be("1")
    bar.should be_true
    sub2.should be_true
    z.should be_true
  end

  it "unregisters subcommands on call" do
    foo = false
    bar = false
    baz = false
    OptionParser.parse(%w(foo baz)) do |opts|
      opts.on("foo", "") do
        foo = true
        opts.on("bar", "") { bar = true }
      end
      opts.on("baz", "") { baz = true }
    end
    foo.should be_true
    bar.should be_false
    baz.should be_false
  end

  it "handles subcommand --help well (top level)" do
    help = nil
    OptionParser.parse(%w(--help)) do |opts|
      opts.banner = "Usage: foo"
      opts.on("subcommand", "Subcommand Description") do
        opts.on("-f", "--foo", "Foo") { }
      end
      opts.on("--verbose", "Verbose mode") { }
      opts.on("--help", "Help") { help = opts.to_s }
    end

    help.should eq <<-USAGE
      Usage: foo
          subcommand                       Subcommand Description
              --verbose                    Verbose mode
              --help                       Help
      USAGE
  end

  it "handles subcommand --help well (subcommand)" do
    help = nil
    OptionParser.parse(%w(subcommand --help)) do |opts|
      opts.banner = "Usage: foo"
      opts.on("subcommand", "Subcommand Description") do
        opts.banner = "Usage: foo subcommand"
        opts.on("-f", "--foo", "Foo") { }
      end
      opts.on("--verbose", "Verbose mode") { }
      opts.on("--help", "Help") { help = opts.to_s }
    end

    help.should eq <<-USAGE
      Usage: foo subcommand
              --verbose                    Verbose mode
              --help                       Help
          -f, --foo                        Foo
      USAGE
  end

  it "handles subcommands with hyphen" do
    subcommand = false
    OptionParser.parse(%w(sub-command)) do |opts|
      opts.banner = "Usage: foo"
      opts.on("sub-command", "Subcommand description") { subcommand = true }
    end

    subcommand.should be_true
  end

  it "stops when asked" do
    args = %w(--foo --stop --bar)
    foo = false
    bar = false
    OptionParser.parse(args) do |opts|
      opts.on("--foo", "") { foo = true }
      opts.on("--bar", "") { bar = true }
      opts.on("--stop", "") { opts.stop }
      opts.unknown_args do |before, after|
        before.should eq(%w())
        after.should eq(%w(--bar))
      end
    end
    foo.should be_true
    bar.should be_false
    args.should eq(%w(--bar))
  end

  it "can run a callback on every argument" do
    args = %w(--foo file --bar)
    foo = false
    bar = false
    OptionParser.parse(args) do |opts|
      opts.on("--foo", "") { foo = true }
      opts.on("--bar", "") { bar = true }
      opts.before_each do |arg|
        if arg == "file"
          opts.stop
        end
      end
      opts.unknown_args do |before, after|
        before.should eq(%w(file))
        after.should eq(%w(--bar))
      end
    end

    foo.should be_true
    bar.should be_false
    args.should eq(%w(file --bar))
  end
end

describe "OptionParser with summary_width and summary_indent" do
  it "formats flags and descriptions with default summary_width and summary_indent" do
    parser = OptionParser.new

    parser.on("-f", "--flag", "A short description") { }
    parser.on("-l", "--long-flag", "A long description for testing purposes") { }

    output = parser.to_s

    expected_output = <<-USAGE
        -f, --flag                       A short description
        -l, --long-flag                  A long description for testing purposes
    USAGE

    output.should eq(expected_output)
  end

  it "formats a flag pair with an empty short flag as a long-only flag" do
    parser = OptionParser.new

    parser.on("-s", "--short", "Short option") { }
    parser.on("", "--long", "Long option") { }

    output = parser.to_s

    expected_output = <<-USAGE
        -s, --short                      Short option
            --long                       Long option
    USAGE

    output.should eq(expected_output)
  end

  it "formats flags and descriptions with custom summary_width and summary_indent" do
    parser = OptionParser.new
    parser.summary_width = 40
    parser.summary_indent = "|" * 6

    parser.on("-f", "--flag", "A short description") { }
    parser.on("-l", "--long-flag", "A long description for testing purposes") { }

    output = parser.to_s

    expected_output = <<-USAGE
    ||||||-f, --flag                               A short description
    ||||||-l, --long-flag                          A long description for testing purposes
    USAGE

    output.should eq(expected_output)
  end

  it "handles multiline descriptions correctly with summary_width" do
    parser = OptionParser.new
    parser.summary_width = 20
    parser.summary_indent = " " * 4

    parser.on("--complex-option", "This is a detailed description\nspanning multiple lines for testing") { }

    output = parser.to_s

    expected_output = <<-USAGE
            --complex-option This is a detailed description
                             spanning multiple lines for testing
    USAGE

    output.should eq(expected_output)
  end

  it "adjusts formatting when flag length exceeds summary_width" do
    parser = OptionParser.new
    parser.summary_width = 20
    parser.summary_indent = " " * 4

    parser.on("--very-very-long-option", "Description that follows a very\nlong flag") { }

    output = parser.to_s

    expected_output = <<-USAGE
            --very-very-long-option
                             Description that follows a very
                             long flag
    USAGE

    output.should eq(expected_output)
  end

  it "formats summary_width boundary cases" do
    parser = OptionParser.new
    parser.summary_width = 20
    parser.summary_indent = " " * 4

    parser.on("-c", "--complex-option", "desc") { }
    parser.on("-d", "--extended-option", "desc") { }
    parser.on("-e", "--val-option VAL", "desc") { }
    parser.on("-f", "--value-opt VALUE", "desc") { }

    output = parser.to_s

    expected_output = <<-USAGE
        -c, --complex-option desc
        -d, --extended-option
                             desc
        -e, --val-option VAL desc
        -f, --value-opt VALUE
                             desc
    USAGE

    output.should eq(expected_output)
  end

  it "handles extreme summary_width values (e.g., 0)" do
    parser = OptionParser.new
    parser.summary_width = 0
    parser.summary_indent = " " * 4

    parser.on("--short", "No space for flags!") { }

    output = parser.to_s

    expected_output = <<-USAGE
            --short
         No space for flags!
    USAGE

    output.should eq(expected_output)
  end

  it "handles extreme summary_indent values (e.g., empty string)" do
    parser = OptionParser.new
    parser.summary_width = 20
    parser.summary_indent = ""

    parser.on("--test", "Indentation removed") { }

    output = parser.to_s

    expected_output = <<-USAGE
        --test           Indentation removed
    USAGE

    output.should eq(expected_output)
  end

  it "raises if summary_width is negative" do
    parser = OptionParser.new
    expect_raises(ArgumentError, "Negative summary width: -10") do
      parser.summary_width = -10
    end
  end

  it "formats subcommand help with custom summary_indent" do
    help = nil
    OptionParser.parse(%w(subcommand --help)) do |opts|
      opts.summary_indent = "||"
      opts.banner = "Usage: foo"

      opts.on("subcommand", "Subcommand description") do
        opts.banner = "Usage: foo subcommand"
        opts.on("--local", "Local flag") { }
      end
      opts.on("other", "Other subcommand") { }
      opts.on("--help", "Help") { help = opts.to_s }
    end

    help.should eq <<-USAGE
      Usage: foo subcommand
      ||    --help                       Help
      ||    --local                      Local flag
      USAGE
  end
end
