require 'mandrill'
require 'mandrill_template/client'
require 'mandrill_template/template'
require 'formatador'
require 'unicode' unless ['jruby'].include?(RbConfig::CONFIG['ruby_install_name'])
require 'yaml'
require "mandrill_template/monkey_create_file"
autoload "Handlebars", 'handlebars'

class MandrillTemplateManager < Thor
  include Thor::Actions
  VERSION = "0.3.0"

  desc "export_all", "export all templates from remote to local files."
  def export_all
    remote_templates = MandrillClient.client.templates.list
    remote_templates.each do |template|
      export(template["slug"])
    end
  end

  desc "export SLUG", "export template from remote to local files."
  def export(slug)
    template = MandrillClient.client.templates.info(slug)
    meta, code, text  = build_template_for_export(template)
    save_as_local_template(meta, code, text)
  end

  desc "upload SLUG", "upload template to remote as draft."
  option :publish, type: :boolean, default: false, aliases: :p
  def upload(slug)
    template = MandrillTemplate::Local.new(slug)
    if template.avail
      upload_template(template)
      publish(slug) if options[:publish]
    else
      puts "Template data not found #{slug}. Please generate first."
    end
  end
  
 desc "upload ALL", "upload all template to remote as draft."
  option :publish, type: :boolean, default: false, aliases: :p
  def upload_all()
    labels = Dir.glob("#{ templates_directory }/*").map {|path| path.split(File::SEPARATOR).last}
    labels.each do |label|
      template = MandrillTemplate::Local.new(label)
      if template.avail
        upload_template(template)
        publish(slug) if options[:publish]
        puts "Template published #{label}. feeling good."
      else
        puts "Template data not found #{label}. Please generate first."
      end
    end 
  end
  desc "delete SLUG", "delete template from remote."
  option :delete_local, type: :boolean, default: false
  def delete(slug)
    begin
      result = MandrillClient.client.templates.delete(slug)
      puts result.to_yaml
    rescue Mandrill::UnknownTemplateError => e
      puts e.message
    end
    delete_local_template(slug) if options[:delete_local]
  end

  desc "generate SLUG", "generate new template files."
  def generate(slug)
    new_template = MandrillTemplate::Local.new(slug)
    puts new_template.class
    meta, code, text = build_template_for_export(new_template)
    save_as_local_template(meta, code, text)
  end

  desc "publish SLUG", "publish template from draft."
  def publish(slug)
    puts MandrillClient.client.templates.publish(slug).to_yaml
  end

  desc "render SLUG [PARAMS_FILE]", "render mailbody from local template data. File should be Array. see https://mandrillapp.com/api/docs/templates.JSON.html#method=render."
  option :handlebars, type: :boolean, default: false
  def render(slug, params = nil)
    merge_vars =  params ? JSON.parse(File.read(params)) : []
    template = MandrillTemplate::Local.new(slug)
    if template.avail
      if options[:handlebars]
        handlebars = Handlebars::Context.new
        h_template = handlebars.compile(template['code'])
        puts h_template.call(localize_merge_vars(merge_vars))
      else
        result = MandrillClient.client.templates.render template.slug,
          [{"content"=>template["code"], "name"=>template.slug}],
          merge_vars
        puts result["html"]
      end
    else
      puts "Template data not found #{slug}. Please generate first."
    end
  end

  desc "list [LABEL]", "show template list both of remote and local [optionally filtered by LABEL]."
  option :verbose, type: :boolean, default: false, aliases: :v
  def list(label = nil)
    puts "Remote Templates"
    puts "----------------------"
    remote_templates = MandrillClient.client.templates.list(label)
    remote_templates.map! do |template|
      template["has_diff"] = has_diff_between_draft_and_published?(template)
      template
    end

    if options[:verbose]
    Formatador.display_compact_table(
      remote_templates,
      ["has_diff",
       "name",
       "slug",
       "publish_name",
       "draft_updated_at",
       "published_at",
       "labels",
       "subject",
       "publish_subject",
       "from_email",
       "publish_from_email",
       "from_name",
       "publish_from_name"]
    )
    else
      Formatador.display_compact_table(
        remote_templates,
        ["has_diff",
         "name",
         "slug",
         "from_email",
         "from_name",
         "subject",
         "labels",
         "from_name"]
      )
    end

    puts "Local Templates"
    puts "----------------------"
    Formatador.display_compact_table(
      collect_local_templates(label),
      [
        "name",
        "slug",
        "from_email",
        "from_name",
        "subject",
        "labels"
      ]
    )
  end

  private

  def has_diff_between_draft_and_published?(t)
    %w[name code text subject].each do |key|
      return true if t[key] != t["publish_#{key}"]
    end
    return true unless t['published_at']
    false
  end

  def build_template_for_export(t)
    [
      {
        "name"       => t['name'],
        "slug"       => t['slug'],
        "labels"     => t['labels'],
        "subject"    => t['subject'],
        "from_email" => t['from_email'],
        "from_name"  => t['from_name']
      },
      t['code'],
      t['text']
    ]
  end

  def templates_directory
    MandrillClient.templates_directory
  end

  def save_as_local_template(meta, code, text)
    dir_name = meta['slug']
    empty_directory File.join(templates_directory, dir_name)
    create_file File.join(templates_directory, dir_name, "metadata.yml"), meta.to_yaml
    create_file File.join(templates_directory, dir_name, "code.html"), code
    create_file File.join(templates_directory, dir_name, "text.txt"), text
  end

  def collect_local_templates(label = nil)
    local_templates = []
    dirs = Dir.glob("#{ templates_directory }/*").map {|path| path.split(File::SEPARATOR).last}
    dirs.map do |dir|
      begin
        template = MandrillTemplate::Local.new(dir)
        if label.nil? || template['labels'].include?(label)
          local_templates << template
        end
      rescue
        next
      end
    end
    local_templates
  end

  def delete_local_template(slug)
    template = MandrillTemplate::Local.new(slug)
    if template.avail
      template.delete!
    else
      puts "Local template data not found #{slug}."
    end
  end

  def upload_template(t)
    if remote_template_exists?(t.slug)
      method = :update
    else
      method = :add
    end
    result = MandrillClient.client.templates.send(method, t.slug,
      t['from_email'],
      t['from_name'],
      t['subject'],
      t['code'],
      t['text'],
      false, # publish
      t['labels']
    )
    puts result.to_yaml
  end

  def remote_template_exists?(slug)
    begin
      MandrillClient.client.templates.info(slug)
      true
    rescue Mandrill::UnknownTemplateError
      false
    end
  end

  def localize_merge_vars(merge_vars)
    h = {}
    merge_vars.each {|kv| h[kv["name"]] = kv["content"] }
    h
  end
end