#!/usr/bin/env ruby -rubygems
require 'faraday'
require 'trollop'
require 'fileutils'

class ZenDesk2Tender
  include FileUtils
  attr_reader :opts, :conn
  EXPORT_DIR = '.export-data'

  def initialize
    @author_email = {}
    @opts = Trollop::options do
      banner <<-EOM
    Usage:
      #{$0} -e <email> -p <password> -s <subdomain>

    Prerequisites:
      # Ruby gems
      gem install faraday -v "~>0.4.5"
      gem install trollop
      gem install yajl-ruby
      # UNIX tools
      brew install lynx

    Options:
      EOM
      opt :email,     "user email address", :type => String
      opt :password,  "user password",      :type => String
      opt :subdomain, "subdomain",          :type => String
    end

    [:email, :password, :subdomain ].each do |option|
      Trollop::die option, "is required" if opts[option].nil?
    end

    @conn = Faraday::Connection.new("http://#{opts[:subdomain]}.zendesk.com") do |b|
      b.adapter :net_http
      b.response :yajl
    end
    conn.basic_auth(opts[:email], opts[:password])
  end

  def export_users
    response = conn.get('users.json')
    if response.success?
      dir_name = File.join(EXPORT_DIR,'users')
      mkdir_p dir_name
      response.body.each do |user|
        File.open(File.join(dir_name, "#{user['email'].gsub(/\W+/,'_')}.json"), "w") do |file|
          @author_email[user['id']] = user['email']
          file.puts(Yajl::Encoder.encode(
            :name => user['name'],
            :email => user['email'],
            :created_at => user['created_at'],
            :updated_at => user['updated_at'],
            :state => %w[user support support][user['roles']]
          ))
        end
      end
    else
      puts "failed to get users:"
      puts response.inspect
    end
  end

  def export_categories
    response = conn.get('forums.json')
    if response.success?
      dir_name = File.join(EXPORT_DIR,'categories')
      mkdir_p dir_name
      response.body.each do |forum|
        File.open(File.join(dir_name, "#{forum['id']}.json"), "w") do |file|
          file.puts(Yajl::Encoder.encode(
            :name => forum['name'],
            :summary => forum['description']
          ))
        end
        export_discussions(forum['id'])
      end
    else
      puts "failed to get categories:"
      puts response.inspect
    end
  end

  def author_email user_id
    @author_email[user_id] ||= begin
       # the cache should be populated during export_users but we'll attempt
       # to fetch unrecognized ids just in case
       conn.get("users/#{user_id}.json").body['email'] rescue nil
    end
  end

  def dump_body entry, body
    File.open(File.join("tmp", "#{entry['id']}_body.html"), "w") do |file|
      file.write(body)
    end
  end

  def load_body entry
    `lynx -dump file://$PWD/tmp/#{entry['id']}_body.html`
  end

  def export_discussions forum_id
    response = conn.get("forums/#{forum_id}/entries.json")
    if response.success?
      dir_name = File.join(EXPORT_DIR,'categories', forum_id.to_s)
      mkdir_p dir_name
      mkdir_p 'tmp'
      response.body.each do |entry|
        File.open(File.join(dir_name, "#{entry['id']}.json"), "w") do |file|
          comments = entry['posts'].map do |post|
            dump_body entry, post['body']
            {
              :body => load_body(entry),
              :author_email => author_email(post['user_id']),
              :created_at => post['created_at'],
              :updated_at => post['updated_at'],
            }
          end
          dump_body entry, entry['body']
          file.puts(Yajl::Encoder.encode(
            :title    => entry['title'],
            :comments => [{
              :body => load_body(entry),
              :author_email => author_email(entry['submitter_id']),
              :created_at => entry['created_at'],
              :updated_at => entry['updated_at'],
            }] + comments
          ))
          rm "tmp/#{entry['id']}_body.html"
        end
      end
    else
      puts "failed to get entries for forum #{forum_id}:"
      puts response.inspect
    end
  end

  def create_archive
    export_file = "export_#{opts[:subdomain]}.tgz"
    system "tar -zcf #{export_file} -C #{EXPORT_DIR} ."
    system "rm -rf #{EXPORT_DIR}"
    puts "created #{export_file}"
  end

  def self.run
    exporter = new
    exporter.export_users
    exporter.export_categories
    exporter.create_archive
  end

end

ZenDesk2Tender.run

