require 'thor'
require "pathname"
require "yaml"
require "date"
require 'fileutils'

# @author Brandon Pittman
# This is the main class that interfaces with Thor's methods and does all the heavy lifting for Nikki.
# It's a bit of a "God" object. Sorries.
class Generator < Thor

  desc "setup", "Creates new Nikki and config files."
  # This methods creates the ".nikki" directory, config file and journal file.
  def setup
    create_path
    create_file
    create_config_file
  end

  desc "new ENTRY", "Creates a new entry in the Nikki journal."
  # Add entry to journal
  # @param entry [String] entry to add to the journal
  # Will open your configured text editor on OS X if you didn't update the journal the previous day.
  # This will allow you to add missing entries in bulk.
  # It reads the settings in from the config YAML file and changes the date updated.
  # It does the same with the journal file, reading in the YAML and merging the hash of entries, and then saves the YAML back again.
  # There's also a method to check off a corresponding task in OmniFocus at the end.
  def new(*args)
    settings = read_config
    settings[:updated] = today
    entry = args.join(" ")
    entry_hash = { today => entry }
    journal = read_file.merge(entry_hash)
    write_file(journal)
    open unless updated_yesterday?
    write_config(settings)
    add_to_omnifocus
    puts latest
  end

  desc "open", "Open current year's journal file in editor."
  option :marked, :aliases => :m, :type => :boolean
  # Open Nikki journal in configured text editor
  def open
    if options[:marked]
      %x{open -a Marked #{file}}
    else
      %x{open -a "#{editor}" #{file}}
    end
  end

  desc "config", "Change Nikki's settings."
  option :editor, :aliases => :e, :banner => "Set default editor to open journal file in."
  option :yesterday, :aliases => :y, :type => :boolean, :banner => "Set nikki's \"updated\" date to yesterday."
  option :today, :aliases => :t, :type => :boolean, :banner => "Set nikki's \"updated\" date to today."
  option :print, :aliases => :p, :type => :boolean, :banner => "Prints nikki's config settings."
  option :latest, :aliases => :l, :type => :boolean, :banner => "Prints latest journal entries."
  # Configure Nikki's settings
  # @param --editor [String] (read_config[:editor]) Sets Nikki's editor to open journal file
  # @param --yesterday Set `settings[:updated]` to yesterday
  # @param --today Set `settings[:updated]` to today
  # @param --print Prints Nikki's configuration settings to STDOUT
  # @param --latest Prints Nikki's latest entries to STDOUT
  def config
    settings = read_config
    settings[:editor] = options[:editor] || read_config[:editor]
    settings[:updated] = Date.today-1 if options[:yesterday]
    settings[:updated] = Date.today if options[:today]
    write_config(settings)
    puts settings.to_yaml if options[:print]
    puts latest if options[:latest]
  end

  no_commands do
    def path
      Pathname.new("#{ENV['HOME']}/.nikki/")
    end

    def create_path
      path.mkdir unless path.exist?
    end

    def config_file
      path.join("nikki.config.yaml")
    end

    def read_config
      create_path
      config_file_exist?
      YAML.load(config_file.read)
    end

    def write_config(hash)
      config_file.open('w') { |t| t << hash.to_yaml }
    end

    def read_file
      create_path
      file_exist?
      YAML.load(file.read)
    end

    def write_file(hash)
      file.write(hash.to_yaml)
    end

    def editor
      read_config[:editor]
    end

    def file
      path.join("nikki.yaml")
    end

    def config_file_exist?
      return true if config_file.exist?
      create_config_file
      return true
    end

    def file_exist?
      return true if file.exist?
      create_file
      return true
    end

    def create_config_file
      settings = {}
      settings[:updated] = today
      settings[:editor] = 'TextEdit'
      write_config(settings)
    end

    def create_file
      FileUtils.touch(file)
    end

    def marked
      Pathname.new("/Applications/Marked.app")
    end

    def today
      Date.today
    end

    def months_with_names
      {1=>{:name=>"January"},
      2=>{:name=>"February"},
      3=>{:name=>"March"},
      4=>{:name=>"April"},
      5=>{:name=>"May"},
      6=>{:name=>"June"},
      7=>{:name=>"July"},
      8=>{:name=>"August"},
      9=>{:name=>"September"},
      10=>{:name=>"October"},
      11=>{:name=>"November"},
      12=>{:name=>"December"}}
    end

    def leap_year?
      Date.today.leap?
    end

    def last_updated
      read_config[:updated]
    end

    def updated_yesterday?
      last_updated == Date.today-1
    end

    def latest
      list = []
      (0..3).each do |n|
        list << "#{today-n}: #{read_file[today-n]}"
      end
      list.reverse!
    end

    def add_to_omnifocus
      %x{osascript <<-APPLESCRIPT
        tell application "OmniFocus"
          tell default document
            set nikki_task to first remaining task of flattened context "House" whose name is "Record what I learned today"
            set completed of nikki_task to true
          end tell
        end tell
      APPLESCRIPT}
    end
  end
end