require 'net/http'
require 'uri'
require 'fileutils'


# Based on https://github.com/emberjs/ember-rails/blob/master/lib/generators/ember/install_generator.rb
module Ember
  module Generators
    class InstallGenerator < ::Rails::Generators::Base

      class InvalidChannel < ::Thor::Error; end
      class ConflictingOptions < ::Thor::Error; end
      class Deprecated < ::Thor::Error; end
      class InsufficientOptions < ::Thor::Error; end

      ::InvalidChannel = InvalidChannel
      ::ConflictingOptions = ConflictingOptions
      ::Deprecated = Deprecated
      ::InsufficientOptions = InsufficientOptions

      desc "Install Ember.js into your vendor folder"
      class_option :channel,
        :type => :string,
        :required => false,
        :desc => "Ember release channel Choose between 'release', 'beta' or 'canary'"
      class_option :ember_only,
        :type => :boolean,
        :required => false,
        :desc => "Only download Ember.",
        :aliases => '--ember'
      class_option :ember_data_only,
        :type => :boolean,
        :required => false,
        :desc => "Only download ember-data",
        :aliases => '--ember-data'
      class_option :tag,
        :type => :string,
        :required => false,
        :desc => "Download tagged release use syntax v1.0.0-beta.3/ember-data & v1.0.0-rc.8/ember"

      def initialize(args = [], options = {}, config = {})
        super(args, options, config)
        check_options
        process_options
      end


      def ember
        return if options.ember_data_only?

        get_ember_for(:development)
        get_ember_for(:production)
      rescue Thor::Error
        say('WARNING: no ember files on this channel or tag' , :yellow)
      end

      def ember_data
        return if options.ember_only?

        get_ember_for(:development, ember_data_channel, 'ember-data')
        get_ember_for(:production, ember_data_channel, 'ember-data')
      rescue Thor::Error
        say('WARNING: no ember-data files on this channel or tag' , :yellow)
      end

      private

      def get_ember_for(environment, chan = channel, name = 'ember')
        file_name = environment == :production ? "#{name}.prod.js" : "#{name}.js"
        create_file "vendor/assets/javascripts/#{file_name}" do
          fetch "#{base_url}/#{chan}/#{file_name_for(name, environment)}", "vendor/assets/javascripts/#{file_name}"
        end
      end

      def ember_data_channel
        if channel == :release
          say_status("warning:", 'Ember Data is not available on the :release channel. Falling back to beta channel.' , :yellow)
          :beta
        else
          channel
        end
      end

      def file_name_for(component,environment)
        case environment
        when :production
          "#{component}.min.js"
        when :development
          "#{component}.js"
        end
      end

      def check_options
        if options.channel? && !%w(release beta canary).include?(options[:channel])
          say 'ERROR: channel can either be release, beta or canary', :red
          raise InvalidChannel
        end
        if options.channel? && options.tag?
          say 'ERROR: conflicting options. --tag and --channel. --tag is incompatible with other options', :red
          raise ConflictingOptions
        end
        if options.tag? && !(options.ember_only? || options.ember_data_only?)
          say 'ERROR: insufficient options. --tag needs to be combined with eithe --ember or --ember-data', :red
          raise InsufficientOptions
        end
      end

      def process_options
        if options.tag?
          @channel = "tags/#{options.tag}"
        end
      end

      def base_url
        'http://builds.emberjs.com'
      end

      def channel
        if options.channel
          @channel ||= options[:channel]
        else
          @channel ||= :release
        end
      end

      def fetch(from, to)
        message = "#{from} -> #{to}"
        say_status("downloading:", message , :green)

        uri = URI(from)
        output = StringIO.new
        output.puts "// Fetched from channel: #{channel}, with url " + uri.to_s
        output.puts "// Fetched on: " + Time.now.utc.iso8601.to_s
        response = Net::HTTP.get_response(uri)
        case response.code
        when '404'
          say "ERROR: Error reading the content from the channel with url #{from}. File not found" , :red
          raise Thor::Error
        when '200'
          output.puts response.body.force_encoding("UTF-8")
        else
          say "ERROR: Unexpected error with status #{response.code} reading the content from the channel with url #{from}." , :red
          raise Thor::Error
        end
        output.rewind
        output.read
      end
    end
  end
end
