module Ocp::Registry

  class ApiController < Sinatra::Base

    set :root, File.join(File.dirname(__FILE__), '../../')

  	not_found do
      exception = request.env["sinatra.error"]
      @logger.info("[RECEIVED] #{request.request_method} : #{request.url} - #{request.ip}")
      @logger.info(exception.message)
      do_response({:status => "not_found"}, nil)
    end

    error do
      exception = request.env["sinatra.error"]
      @logger.info(exception)
      status(500)
      do_response({:status => "error"}, nil)
    end

    error Ocp::Registry::Error do
      error = request.env["sinatra.error"]
      @logger.info(error.message)
      status(error.code)
      do_response({:status => "error", :message => error.message},nil)
    end

  	# get application list
  	get '/v1/applications' do
      @logger.info("[RECEIVED] #{request.request_method} : #{request.url} - #{request.ip}")
      email = params[:email]
      protected! unless email
      if email
        data = {:status => "error", :message => "Sorry, list all applications of a specific user is not supported any more since security risks"}
  		  do_response(data)
      else
        data = []
        result = @application_manager.list
        result.each do |app|
          data << app.to_hash
        end
        do_response(data, :list, :review => true)
      end
  	end

    get '/' do
      redirect to('/v1/applications/default')
    end

    # check project name 
    post '/v1/applications/check' do
      @logger.info("[RECEIVED] #{request.request_method} : #{request.url} - #{request.ip}")
      if project = params[:project] 
        result = @application_manager.existed_tenant?(project)
        do_response(!result, nil)
      end
    end

  	# get an application detail
  	get '/v1/applications/:id' do
      @logger.info("[RECEIVED] #{request.request_method} : #{request.url} - #{request.ip}")
      if(params[:id] == "default")
        do_response(@application_manager.default, :apply)
      else
    		application = @application_manager.show(params[:id])
        if("true" == params[:review])
          protected!
          if "true" == params[:modified]
      		  data = application.to_hash(:lazy_load => false, :limit => 20) 
            view = :admin_review
          else
            data = application.to_hash(:lazy_load => false, :limit => 10)
            view = :admin_review
          end
        else
          if "true" == params[:modified]
            data = application.to_hash(:lazy_load => false, :limit => 20)
            view = :applicant_review
          else
            data = application.to_hash(:lazy_load => false, :limit => 10)
            view = :view
          end
        end
        do_response(data,view)
      end
  	end

  	# create an application
  	post '/v1/applications' do
      app_info = Yajl.load(request.body.read)

      check_fields = ["email","project","settings"]
      valid, fields = validate_not_null(check_fields,app_info)
      return do_response({:status => "error", :message => "Filed [#{fields.join(", ")}] can not be null"}) unless valid

      check_fields = ["email"]
      valid, fields = validate_email(check_fields,app_info)
      return do_response({:status => "error", :message => "Field [#{fields.join(", ")}] is not a valid email address"}) unless valid

      @logger.info("[RECEIVED] #{request.request_method} : #{request.url} - #{request.ip} : #{app_info}")

      if app_info.kind_of?(Hash) && app_info['settings'].kind_of?(Hash)
        default = Yajl::load(@application_manager.default[:registry_settings].first[:settings])
        settings = default.merge(app_info['settings'])
        app_info["settings"] = json(settings)
      end
  		application = @application_manager.create(app_info)
      app_info = application.to_hash
  		do_response(app_info, nil)
  	end

  	# approve an application
  	post '/v1/applications/:id/approve' do
  	  protected!
      @logger.info("[RECEIVED] #{request.request_method} : #{request.url} - #{request.ip}")
  	  result = @application_manager.approve(params[:id])
      do_response(result.to_hash, nil)
  	end

  	# refuse an application
  	post '/v1/applications/:id/refuse' do
      @logger.info("[RECEIVED] #{request.request_method} : #{request.url} - #{request.ip}")

  	  protected!
  	  body = Yajl.load(request.body.read)
      comments = nil
      comments = body['comments'] if body && body['comments']
      result = @application_manager.refuse(params[:id], comments)
      do_response(result.to_hash, nil)
  	end

    post '/v1/applications/:id/settings' do
      setting = Yajl.load(request.body.read)
      @logger.info("[RECEIVED] #{request.request_method} : #{request.url} - #{request.ip} : #{setting}")

      setting = {} if setting.nil?

      if setting["from"] && setting["from"].strip.upcase == "ADMIN"
        protected! 
        setting["from"] = "ADMIN"
      else
        setting["from"] = "USER"
      end
      result = @application_manager.add_setting_for(params[:id],setting)
      do_response(result.to_hash)
    end

    get '/v1/applications/:id/settings' do
      @logger.info("[RECEIVED] #{request.request_method} : #{request.url} - #{request.ip}")

      result = @application_manager.list_settings(params[:id])

      data = []
      result.each do |setting|
        data << setting.to_hash
      end

      do_response(data)
    end

    post '/v1/applications/:id/cancel' do
      @logger.info("[RECEIVED] #{request.request_method} : #{request.url} - #{request.ip}")

      result = @application_manager.cancel(params[:id])
      do_response(result.to_hash)
    end

  	def initialize
      super
      @logger = Ocp::Registry.logger
      @users = Set.new
      @users << [Ocp::Registry.http_user, Ocp::Registry.http_password]
      @application_manager = Ocp::Registry.application_manager
    end

    private

    def do_response(data, view = nil, mark = nil)
      if request.accept?('application/json') || view.nil? || (data.is_a?(Hash)&&'error' == data[:status])
        @logger.info("[RESPONSE] JSON : json - #{data}")
        json(data)
      else
        @logger.info("[RESPONSE] VIEW : #{view.to_s} - #{data}")
        erb :base do 
          erb view ,:locals => {:data => data ,:mark => mark}
        end
      end

    end

    def protected!
      unless authorized?
        headers("WWW-Authenticate" => 'Basic realm="OCP Registry"')
        halt(401, json("message" => "access_denied"))
      end
    end

    def authorized?
      @auth ||= Rack::Auth::Basic::Request.new(request.env)
      @auth.provided? &&
        @auth.basic? &&
        @auth.credentials &&
        @users.include?(@auth.credentials)
    end

    def json(payload)
      Yajl::Encoder.encode(payload)
    end

    def validate_not_null(fields=[], source={})
      validate(fields, source){|value| !value.nil? }
    end

    def validate_email(fields=[], source={})
      validate(fields, source){|value| value =~ Ocp::Registry::Common::EMAIL_REGEX}
    end

    def validate(fields,source)
      return true if fields.empty?
      return false ,fields if source.empty? || !block_given?

      not_pass = []
      fields.each do |field|  
        not_pass << field unless yield source[field]
      end

      if not_pass.empty?
        return true, []
      else
        return false , not_pass
      end
    end

  end
end