require 'thor'

# Class that deals with the CLI interface.
#
class Ydl::CLI < Thor
  include Ydl::Formatter

  class_option :verbose, type: :boolean, default: false,
    desc: "show verbose output generated by the program"

  def initialize *args
    super

    # ensure that the delegator is ready for a new command
    delegator.reset_for_next_command
    # Ydl::Videos.reload!

    # check if the current command is safe and valid?
    safe, valid = safe_and_valid?
    return if safe

    # err out with usage if Ydl has not been initialized
    error! "You must run 'ydl init' first!", true unless initialized?

    # err out with usage if an unknown command was requested
    error! "Command not found!", true unless valid
  end

  desc 'help', "display the help message for Ydl"
  def help tip = true
    print "Available "
    super()
    puts "Tip: You must run `ydl init` before you can run other commands." if tip
  end

  # Initialize Ydl on the user's machine, and update youtube-dl, as well.
  #
  # At the moment, no checks are performed to ensure that Ydl is compatible
  # with the given machine, but in the future, it will be so.
  #
  desc 'init', "initialize youtube-dl and companion on this system."
  method_option :update, type: :boolean, default: true,
    desc: "Whether to update 'youtube-dl' program?"
  def init

    # greet the owner.
      question = <<-CONTENT.gsub(/^\s{8}/, '').strip
        Welcome to YDL. The Youtube-DL companion!
        I would love to assist you in your quest, but first, let me ask you:
        Where should I download the videos? (Give me a folder!)
        Note that, this directory will be created, if it does not exist!
        [default: <current-directory>]
      CONTENT

      download_dir = File.expand_path ask(question).strip
      classifier   = "%(extractor)s/%(title)s-%(id)s-%(age_limit)s.%(ext)s"

      # let the owner know, what mischief we are upto :)
      puts "\nAlright! That's it :)\nI will do the initial house-keeping for you.\n\n"

      # find the path to the youtube-dl program
      question = <<-CONTENT.gsub(/^\s{10}/, '')
        Seems like this is not my brightest day.. :(
        I was unable to find a valid path to the youtube-dl program on your machine.
        Can you tell me where it is located?
      CONTENT
      bin_path = File.expand_path ask(question).strip if delegator.unknown?

      # enough talk! get to work.
      setup_ydl! download_path: download_dir, classifier: classifier, bin_path: bin_path
      upgrade! if options[:update]
  end

  # TODO: and, maybe the companion, itself?
  desc 'update', "update youtube-dl on this system."
  def update
    upgrade!
  end

  desc 'add [PATH1] [PATH2] [URL]..', "add videos from the given files and supplied urls"
  method_option :piped, type: :boolean, default: false,
    desc: "display progress for all videos separately to enable piping support"
  def add *paths
    # urls that were fed to us
    urls = prepare_list_of_urls_from_arguments(paths)
    debug "Adding #{urls.count} video(s) in the database."

    # urls that have already been added
    existing = Ydl::Videos.already_added_urls_in(urls)
    debug "Found #{existing.count} existing video(s) in the database from this list."

    # new urls that were added in this iteration
    added = add_videos_and_display_progress(urls, existing, options)
    success "Added #{added.count} video(s)."

    # discarded or errored urls
    discarded = urls.count - added.count
    warning "Discarded #{discarded} video(s)." if discarded > 0

    # return the urls which were added to the database
    added | existing
  end

  desc 'search [KEYWORDS]', "search and display videos with the given keywords"
  method_option :eid, type: :string, desc: "return videos matching the given video (extractor) ID"
  method_option :extractor, type: :string, desc: "filter down the matches based on the given extractor"
  method_option :url, type: :string, desc: "return videos matching the given video url"
  method_option :limit, type: :numeric, default: 10,
    desc: "limit the number of matching results returned by this command (default: 10)"
  def search *keywords
    # 1st element: list of videos matching the request
    # 2nd element: fuzzy matching statistics
    # 3rd element: Sequel's Query selector
    matched, stats, query = Ydl::Searcher.new(keywords, options).run
    ( warning "No videos found :("; return ) if matched.empty?

    display_search_results matched
    debug "Displaying a total of #{matched.count} videos."
  end

  desc 'download [PATH1] [PATH2] [URL]..', "download and add videos from the given files and supplied urls"
  method_option :piped, type: :boolean, default: false,
    desc: "display progress for all videos separately to enable piping support"
  def download *paths
    # first, add the videos to the database
    urls = invoke :add, paths

    ( warning "Nothing to download :("; return ) if urls.empty?
    debug "Downloading #{urls.count} video(s)."

    existing = Ydl::Videos.completed.where url: urls
    debug "Found #{existing.count} existing video(s) from this list."

    # Download the pending videos
    downloaded = download_videos_and_display_progress(urls, options[:verbose])
    success "Downloaded #{downloaded.count} video(s)."

    discarded = urls.count - downloaded.count
    warning "Discarded #{discarded} video(s)." if discarded > 0
  end


  default_task :help

  no_tasks {
    private

    def error! message, show_usage = false
      warning "Error Occurred: #{message}\n\n"
      invoke :help, [false] if show_usage
      exit(128)
    end

    def safe_and_valid?
      sub = @_initializer[2][:current_command].name rescue nil
      return [ false, false ] unless sub

      safe  = ["init", "help"]
      valid = self.class.tasks.keys
      [ safe.include?(sub), valid.include?(sub) ]
    end
  }
end
