require 'inherited_resources'

module Softwear
  module Lib
    class ApiController < ActionController::Base
      include ::InheritedResources::Actions
      include ::InheritedResources::BaseHelpers
      extend  ::InheritedResources::ClassMethods
      extend  ::InheritedResources::UrlHelpers

      skip_before_filter :authenticate_user!
      skip_before_filter :verify_authenticity_token

      before_filter :allow_origin

      respond_to :json
      self.responder = InheritedResources::Responder

      self.class_attribute :resource_class, :instance_writer => false unless self.respond_to? :resource_class
      self.class_attribute :parents_symbols,  :resources_configuration, :instance_writer => false

      def self.base_class
        Softwear::Lib::ApiController
      end

      def self.token_authenticate(user_class, options = {})
        include Softwear::Auth::TokenAuthentication
        self.user_class = user_class
        self.token_auth_options = options
        prepend_before_filter :token_authenticate_user!
      end

      def index(&block)
        yield if block_given?

        key_values = (permitted_attributes + [:id]).uniq.map do |a|
          [a, params[a]] if params[a]
        end
          .compact

        self.records ||= resource_class
        self.records = records.where(Hash[key_values])

        respond_to do |format|
          format.json(&render_json(records))
        end
      end

      def show
        super do |format|
          format.json(&render_json)
        end
      end

      def update
        self.record = resource_class.find(params[:id])

        permitted_attributes.each do |a|
          begin
            record.send("#{a}=", params[a]) if params[a]
          rescue ActiveRecord::AssociationTypeMismatch
            # If you try to send "job" as an attribute to order, we're assuming it's
            # not intentional. Send "job_attributes" or update the job model separately
            # to actually update the job.
          end
        end

        if record.save
          respond_to { |format| format.json(&render_json) }
        else
          respond_to { |format| format.json(&render_errors) }
        end
      end

      def create
        create! do |success, failure|
          success.json do
            headers['Location'] = resource_url(record.id)
            render_json(status: 201).call
          end
          failure.json(&render_errors)
        end
      end

      def options
        head(:ok) if request.request_method == 'OPTIONS'
      end

      protected

      def base_class
        self.class.base_class
      end

      def render_json(options = {})
        proc do
          if options.is_a?(Hash)
            records = nil
          else
            records = options
            options = {}
          end
          rendering = {
            json: (records || record),
            methods: (['id'] + permitted_attributes).uniq,
            include: includes
          }
            .merge(options)

          render rendering
        end
      end

      def render_errors(options = {})
        proc do
          rendering = {
            json: { errors: record.errors },
            status: :unprocessable_entity
          }
            .merge(options)

          render rendering
        end
      end

      def self.model_name
        name.gsub('Api::', '').gsub('Controller', '').singularize
      end

      # Override this to specify the :include option of rendering json.
      def includes
        {}
      end

      def records
        instance_variable_get("@#{resource_class.model_name.collection}")
      end

      def records=(r)
        instance_variable_set("@#{resource_class.model_name.collection}", r)
      end

      def record
        instance_variable_get("@#{resource_class.model_name.element}")
      end

      def record=(r)
        instance_variable_set("@#{resource_class.model_name.element}", r)
      end

      def allow_origin
        headers['Access-Control-Allow-Origin'] = '*'
        headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, PATCH, DELETE'
        headers['Access-Control-Allow-Headers'] = 'X-Requested-With'
        headers['Access-Control-Allow-Credentials'] = 'true'
      end

      def permitted_attributes
        resource_class.column_names + ['state_event']
      end

      def permitted_params
        { resource_class.model_name.element => permitted_attributes }
      end
    end
  end
end