require "sinatra/base"
require 'rack/accept_media_types'
require 'db/mongo/models/policy'

module Sinatra

  class Base
    class << self

      @@policies = {}

      # options:
      #   dependencies - array
      def define_policy name, description, options={}
        options[:description] = description
        @@policies[name.to_sym] = Devops::Model::Policy.new(name, options)
      end

      def policies
        @@policies
      end

      def get_with_headers path, opt={}, &block
        before path do
          check_headers :accept
          protect!
        end

        get path, opt, &block
      end

      def post_with_headers path, opt={}, &block
        route_with_headers "POST", path, opt, [:accept, :content_type], &block
      end

      def post_with_statistic path, opt={}, &block
        post path, opt, &block

        after path do
          insert_statistic
        end
      end

      def put_with_headers path, opt={}, &block
        route_with_headers "PUT", path, opt, [:accept, :content_type], &block
      end

      def patch_with_headers path, opt={}, &block
        route_with_headers "PATCH", path, opt, [:accept, :content_type], &block
      end

      def delete_with_headers path, opt={}, &block
        route_with_headers "DELETE", path, opt, [:accept], &block
      end

      def route_with_headers method, path, opt={}, headers, &block
        before path do
          check_headers *headers
          protect!
        end

        route(method, path, opt, &block)

        after path do
          insert_statistic
        end
      end

      def multi_routes path, hash={}
        before path do
          if request.get? or request.delete?
            check_headers :accept
          else
            check_headers :accept, :content_type
          end
          protect!
        end

        hash.each do |method, block|
          route(method, path, {}, &block)
        end

        after path do
          insert_statistic unless request.get?
        end
      end

      def routes
        @routes
      end

      def routes_list
        routes.each do |verb, signature|
          signature.each do |s|
            puts "#{verb}\t#{s[0]}"
          end
        end
      end

    end

    # Check request headers
    def check_headers *headers
      ha = (headers.empty? ? [:accept, :content_type] : headers)
      ha.each do |h|
        case h
        when :accept, "accept"
          accept_json
        when :content_type, "content_type"
          request_json
        end
      end
    end

    # Check Accept header
    #
    # Can client works with JSON?
    def accept_json
      types = request.accept_media_types
      unless types.include?('application/json') or types.include?("*/*")
        response.headers['Accept'] = 'application/json'
        halt_response("Accept header should contains 'application/json' type", 406)
      end
    end

    # Check Content-Type header
    def request_json
      halt_response("Content-Type should be 'application/json'", 415) if request.media_type.nil? or request.media_type != 'application/json'
    end

  end
end
