# frozen_string_literal: true

# @@finish
module Doing
  # finish command methods
  class FinishCommand
    def initialize(wwid)
      @wwid = wwid
    end

    def add_examples(cmd)
      cmd.example 'doing finish', desc: 'Mark the last entry @done'
      cmd.example 'doing finish --auto --section Later 10', desc: 'Add @done to any unfinished entries in the last 10 in Later, setting the finish time based on the start time of the task after it'
      cmd.example 'doing finish --search "a specific entry" --at "yesterday 3pm"', desc: 'Search for an entry containing string and set its @done time to yesterday at 3pm'
    end

    def add_options(cmd)
      cmd.desc 'Include date'
      cmd.switch [:date], negatable: true, default_value: true

      cmd.desc 'Backdate completed date to date string [4pm|20m|2h|yesterday noon]'
      cmd.arg_name 'DATE_STRING'
      cmd.flag %i[b back started], type: DateBeginString

      cmd.desc 'Overwrite existing @done tag with new date'
      cmd.switch %i[update], negatable: false, default_value: false

      cmd.desc 'Remove @done tag'
      cmd.switch %i[r remove], negatable: false, default_value: false

      cmd.desc 'Finish last entry (or entries) not already marked @done'
      cmd.switch %i[u unfinished], negatable: false, default_value: false

      cmd.desc %(Auto-generate finish dates from next entry's start time.
      Automatically generate completion dates 1 minute before next item (in any section) began.
      --auto overrides the --date and --back parameters.)
      cmd.switch [:auto], negatable: false, default_value: false

      cmd.desc 'Archive entries'
      cmd.switch %i[a archive], negatable: false, default_value: false

      cmd.desc 'Section'
      cmd.arg_name 'NAME'
      cmd.flag %i[s section], multiple: true

      cmd.desc 'Select item(s) to finish from a menu of matching entries'
      cmd.switch %i[i interactive], negatable: false, default_value: false
    end

    def handle_from(options)
      options[:from] = options[:from].split(/#{REGEX_RANGE_INDICATOR}/).map do |time|
        time =~ REGEX_TIME ? "today #{time.sub(/(?mi)(^.*?(?=\d+)|(?<=[ap]m).*?$)/, '')}" : time
      end.join(' to ').split_date_range
      start_date, finish_date = options[:from]
      finish_date ||= Time.now
      [start_date, finish_date]
    end

    def handle_date_options(options)
      if options[:took]
        took = options[:took]
        raise InvalidTimeExpression, 'Unable to parse date string for --took' if took.nil?

      end

      if options[:at]
        finish_date = options[:at]
        finish_date = finish_date.chronify(guess: :begin) if finish_date.is_a? String
        raise InvalidTimeExpression, 'Unable to parse date string for --at' if finish_date.nil?

        start_date = options[:took] ? finish_date - took : nil
      elsif options[:back]
        start_date = options[:back]
        finish_date = options[:took] ? start_date + took : Time.now

        raise InvalidTimeExpression, 'Unable to parse date string' if start_date.nil?

      else
        start_date = options[:took] ? Time.now - took : nil
        finish_date = Time.now
      end

      [start_date, finish_date]
    end
  end
end

desc 'Mark last X entries as @done'
long_desc 'Marks the last X entries with a @done tag and current date. Does not alter already completed entries.'
arg_name 'COUNT', optional: true
command :finish do |c|
  cmd = Doing::FinishCommand.new(@wwid)
  cmd.add_examples(c)
  cmd.add_options(c)
  add_options(:search, c)
  add_options(:tag_filter, c)
  add_options(:finish_entry, c)

  c.action do |_global_options, options, args|
    options[:fuzzy] = false
    unless options[:auto]
      if options[:from]
        start_date, finish_date = cmd.handle_from(options)
      else
        start_date, finish_date = cmd.handle_date_options(options)
      end
    end

    tags = options[:tag] || []

    raise InvalidArgument, 'Only one argument allowed' if args.length > 1

    unless args.empty? || args[0] =~ /\d+/
      raise InvalidArgument, 'Invalid argument (specify number of recent items to mark @done)'

    end

    count = if options[:interactive]
              0
            else
              args[0] ? args[0].to_i : 1
            end

    search = nil

    if options[:search]
      search = options[:search]
      search.sub!(/^'?/, "'") if options[:exact]
    end

    opts = {
      archive: options[:archive],
      back: start_date,
      case: options[:case].normalize_case,
      count: count,
      date: options[:date],
      done_date: finish_date,
      fuzzy: options[:fuzzy],
      interactive: options[:interactive],
      not: options[:not],
      remove: options[:remove],
      search: search,
      section: options[:section],
      sequential: options[:auto],
      start_date: start_date,
      tag: tags,
      tag_bool: options[:bool].normalize_bool,
      tags: ['done'],
      took: options[:took],
      unfinished: options[:unfinished],
      update: options[:update],
      val: options[:val]
    }

    @wwid.tag_last(opts)
  end
end