#!/usr/bin/env ruby
$LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
require 'doing/cli_status'

require 'shellwords'

class ::String
  def short_desc
    split(/[,.]/)[0].sub(/ \(.*?\)?$/, '').strip
  end

  def ltrunc(max)
    if length > max
      sub(/^.*?(.{#{max - 3}})$/, '...\1')
    else
      self
    end
  end

  def ltrunc!(max)
    replace ltrunc(max)
  end
end

class ZshCompletions
  include Status

  attr_accessor :commands, :global_options

  def generate_helpers
    out=<<~EOFUNCTIONS
      compdef _doing doing

      function _doing() {
          local line state

          function _commands {
              local -a commands

              commands=(
                        #{generate_subcommand_completions.join("\n                  ")}
              )
              _describe 'command' commands
          }

          _arguments -C \
                  "1: :_commands" \
                  "*::arg:->args"



          case $line[1] in
              #{generate_subcommand_option_completions(indent: '            ').join("\n            ")}
          esac

          _arguments -s $args
      }

    EOFUNCTIONS
    status('Complete', reset: false)
    out
  end

  def get_help_sections(command = '')
    res = `doing help #{command}`.strip
    scanned = res.scan(/(?m-i)^([A-Z ]+)\n([\s\S]*?)(?=\n+[A-Z]+|\Z)/)
    sections = {}
    scanned.each do |sect|
      title = sect[0].downcase.strip.gsub(/ +/, '_').to_sym
      content = sect[1].split(/\n/).map(&:strip).delete_if(&:empty?)
      sections[title] = content
    end
    sections
  end

  def parse_option(option)
    res = option.match(/(?:-(?<short>\w), )?(?:--(?:\[no-\])?(?<long>[\w_]+)(?:=(?<arg>\w+))?)\s+- (?<desc>.*?)$/)
    return nil unless res

    {
      short: res['short'],
      long: res['long'],
      arg: res[:arg],
      description: res['desc'].short_desc
    }
  end

  def parse_options(options)
    options.map { |opt| parse_option(opt) }
  end

  def parse_command(command)
    res = command.match(/^(?<cmd>[^, \t]+)(?<alias>(?:, [^, \t]+)*)?\s+- (?<desc>.*?)$/)
    commands = [res['cmd']]
    commands.concat(res['alias'].split(/, /).delete_if(&:empty?)) if res['alias']

    {
      commands: commands,
      description: res['desc'].short_desc
    }
  end

  def parse_commands(commands)
    commands.map { |cmd| parse_command(cmd) }
  end

  def generate_subcommand_completions
    out = []
    # processing = []
    @commands.each_with_index do |cmd, i|
      # processing << cmd[:commands].first
      processing = cmd[:commands]
      progress('Processing subcommands', i, @commands.count, processing)
      cmd[:commands].each do |c|
        out << "'#{c}:#{cmd[:description].gsub(/'/, '\\\'')}'"
      end
    end
    clear
    out
  end

  def generate_subcommand_option_completions(indent: '        ')

    out = []
    # processing = []

    @commands.each_with_index do |cmd, i|
      # processing << cmd[:commands].first
      processing = cmd[:commands]
      progress('Processing subcommand options', i, @commands.count, processing)

      data = get_help_sections(cmd[:commands].first)
      option_arr = []

      if data[:command_options]
        parse_options(data[:command_options]).each do |option|
          next if option.nil?

          arg = option[:arg] ? '=' : ''

          option_arr << if option[:short]
                          %({-#{option[:short]},--#{option[:long]}#{arg}}"[#{option[:description].gsub(/'/, '\\\'')}]")
                        else
                          %("(--#{option[:long]}#{arg})--#{option[:long]}#{arg}}[#{option[:description].gsub(/'/, '\\\'')}]")
                        end
        end
      end

      cmd[:commands].each do |c|
        out << "#{c}) \n#{indent}    args=( #{option_arr.join(' ')} )\n#{indent};;"
      end
    end

    out
  end

  def initialize
    status('Generating Zsh completions', reset: false)
    data = get_help_sections
    @global_options = parse_options(data[:global_options])
    @commands = parse_commands(data[:commands])
  end

  def generate_completions
    generate_helpers
  end
end

puts ZshCompletions.new.generate_completions