# -*- encoding : utf-8 -*-

require 'phrase/tool_config'

require 'optparse'
require 'net/http'
require 'net/https'
require 'fileutils'

module PhraseGem
  class Tool

    def initialize(argv)
      @args = argv
    end

    def run
      config = ToolConfig.new
      command = args.first

      case command
        when /init/
          init(config)
        when /push/
          push(config)
        when /pull/
          pull(config)
        else
          print_usage
      end
    end

    private
    
    def init(config)
      secret_param = args.find{ |arg| arg =~ /secret=/ }
      unless secret_param.to_s.match(/secret=.+/)
        $stderr.puts "Need a secret to init, but found none."
        $stderr.puts "Please provide the --secret=YOUR_SECRET parameter."
        exit(41)
      end

      secret = secret_param.split("=", 2).last
      config.secret = secret
      puts "Wrote secret to config file .phrase"
      
      default_locale_param = args.find{ |arg| arg =~ /default-locale=/ }
      if default_locale_param.to_s.match(/default-locale=.+/)
        locale_name = default_locale_param.split("=", 2).last
      else 
        locale_name = "en"
      end
      create_locale(locale_name, config)
      make_locale_default(locale_name, config)
    end

    def push(config)
      files = choose_files_to_upload
      if files.empty?
        puts "Could not find any files to upload :("
        exit(43)
      end
      
      if !config.secret || config.secret.empty?
        puts "No config present. Please initialize phrase before pushing or pulling."
        exit(43)
      end
          
      upload_files(files, config)
    end

    def choose_files_to_upload
      file_name = args[1]
      
      unless file_name
        if self.class.rails_default_locale_folder_is_available
          file_name = self.class.rails_default_locale_folder
          puts "No file or directory specified, using #{self.class.rails_default_locale_folder}"
        else 
          $stderr.puts "Need either a file or directory:"
          $stderr.puts "phrase push FILE"
          $stderr.puts "phrase push DIRECTORY"
          exit(46)
        end
      end

      unless File.exist?(file_name)
        $stderr.puts "The file #{file_name} could not be found."
        exit(42)
      end

      if File.directory?(file_name)
        files = Dir.glob("#{File.expand_path(file_name)}/**")
      else
        files = [file_name]
      end
    end

    def upload_files(files, config)
      files.each do |file|
        proceed_with_upload = true
        
        if File.directory?(file)
          proceed_with_upload = false
        end
        
        if is_yaml_file(file)
          proceed_with_upload = false
          $stderr.puts "Notice: Could not upload #{file} (extension not supported - see http://phraseapp.com/help for more information)"
        end
        
        if proceed_with_upload
          puts "Uploading #{file}..."
          params = {
            'auth_token'=>config.secret,
            'filename'=> file,
            'file_content' => File.read(file)
          }

          http = http_client(config)
        
          request = Net::HTTP::Post.new("#{config.api_path_prefix}/translation_keys/upload")
          request.set_form_data(params)
          res = http.request(request)

          dump_summary_to_file(res, file)
          unless res.code.to_s =~ /^[23]/
            print_server_error(res, file)
          end
        end
      end
    end

    def pull(config)
      locale = args[1]
      
      locales = []
      if locale && locale.strip != ''
        locales = [locale]
      else
        locales = fetch_locales(config)
      end
      
      locales.each do |locale|
        fetch_translation_for_locale(config, locale)
      end
    end
    
    def fetch_translation_for_locale(config, locale)
      print "Downloading phrase.#{locale}.yml..."
      ::FileUtils.mkdir_p("phrase/locales")
    
      http = http_client(config)
    
      request = Net::HTTP::Get.new("#{config.api_path_prefix}/translations/download?auth_token=#{config.secret}&locale=#{locale}")
      res = http.request(request)
    
      if res.code.to_s =~ /200/
        puts "OK"
        File.open("phrase/locales/phrase.#{locale}.yml", "w") do |file|
          file.write(res.body)
        end
      else
        puts "Failed"
        print_server_error(res)
      end
    end
    
    def fetch_locales(config)
      http = http_client(config)
      
      request = Net::HTTP::Get.new("#{config.api_path_prefix}/locales?auth_token=#{config.secret}")
      res = http.request(request)
      
      if res.code.to_s =~ /200/
        puts "Fetched all locales"
        return JSON.parse(res.body).map { |locale| locale['name'] }
      else
        puts "Failed"
        print_server_error(res)
        exit(47)
      end
    end
    
    def create_locale(locale_name, config)
      params = {
        'auth_token' => config.secret,
        'locale[name]' => locale_name
      }

      http = http_client(config)
    
      request = Net::HTTP::Post.new("#{config.api_path_prefix}/locales")
      request.set_form_data(params)
      res = http.request(request)
      
      if res.code.to_s =~ /200/
        puts "Created locale \"#{locale_name}\""
      else
        puts "Notice: Locale \"#{locale_name}\" could not be created (maybe it already exists)"
      end
    end
    
    def make_locale_default(locale_name, config)
      params = {
        'auth_token' => config.secret,
      }

      http = http_client(config)
    
      request = Net::HTTP::Put.new("#{config.api_path_prefix}/locales/#{locale_name}/make_default")
      request.set_form_data(params)
      res = http.request(request)
      
      if res.code.to_s =~ /200/
        puts "Locale \"#{locale_name}\" is now the default locale"
      else
        puts "Notice: Locale \"#{locale_name}\" could not be made the default locale"
        print_server_error(res)
      end
    end

    def print_server_error(res, filename=nil)
      error_message = server_error_message(res.body)
      $stderr.puts "Server error: #{res.code} - #{error_message} (#{filename})"
    end

    def server_error_message(body)
      begin
        JSON.parse(body)["error"]
      rescue JSON::ParserError
        "Unknown error"
      end
    end

    def print_usage
      $stderr.puts <<USAGE
Welcome to phrase!

    phrase           Prints usage

    phrase init --secret=<YOUR SECRET> --default-locale=<YOUR DEFAULT LOCALE>

    phrase push FILE
    phrase push DIRECTORY
USAGE
    end

    def args
      @args
    end

    def puts_debug_info
      puts "ARGS: #{args.join(",")}"
      puts "Dir.pwd: #{Dir.pwd}"
    end
    
    def dump_summary_to_file(res, upload_file)
      ::FileUtils.mkdir_p("phrase/uploads/")
      timestamp = Time.now.strftime("%Y%m%m-%H%M%S")
      file_name = "#{File.basename(upload_file)}-#{timestamp}"
      summary_file = "phrase/uploads/#{file_name}.json"
      File.open(summary_file, "w") do |file|
        file.write(res.body)
      end
      puts "Summary saved in #{summary_file}"
    end
    
    def http_client(config)
      http = Net::HTTP.new(config.api_host, config.api_port)
      http.use_ssl = true if config.api_use_ssl == "1"
      http.verify_mode = OpenSSL::SSL::VERIFY_NONE
      http.ca_file = File.join(File.dirname(__FILE__), "..", "..", "cacert.pem")
      http
    end
    
    def is_yaml_file(filepath)
      !File.directory?(filepath) && filepath.split('.').last != 'yml'
    end
    
    def self.rails_default_locale_folder
      "./config/locales/"
    end
    
    def self.rails_default_locale_folder_is_available
      File.exist?(rails_default_locale_folder) && File.directory?(rails_default_locale_folder)
    end
  end
end