class ApplicationController < ActionController::API
  rescue_from ActiveRecord::RecordInvalid, with: :render_record_invalid
  rescue_from ActiveRecord::RecordNotUnique, with: :render_record_not_unique

  before_action :reject_banned_ip_address!
  before_action :authenticate_user
  before_action :reject_banned_user!

  def current_user = @current_user

  private

  def authenticate_user
    code = request.headers['X-Transfer-Code'] || request.headers['HTTP_X_TRANSFER_CODE']
    return if code.blank?

    @current_user = User.find_by(inheritance_code: code)
  end

  def bool? key, default: false
    return default if params[key].nil?

    s = params[key].to_s.strip.downcase
    if default
      !(s.in?(['0', 'false', 'off', 'no']))
    else
      s.in?(['', '1', 'true', 'on', 'yes'])
    end
  end

  def render_bad_request message = 'リクエストが不正です．'
    render json: { type: 'bad_request',
                   message:,
                   errors: { },
                   base_errors: [message] },
           status: :bad_request
  end

  def render_unprocessable_entity message = '入力を確認してください．', field: nil
    render_validation_error(fields: field ? { field => [message] } : { },
                            base: field ? [] : [message])
  end

  def render_record_invalid error
    render_validation_error error.record
  end

  def render_record_not_unique _error = nil
    render_validation_error base: ['すでに存在してゐます．']
  end

  def reject_banned_ip_address!
    ip_address = IpAddress.find_by(ip_address: IPAddr.new(request.remote_ip).hton)
    return unless ip_address&.banned?

    head :forbidden
  end

  def reject_banned_user!
    return unless current_user&.banned?

    head :forbidden
  end

  def render_validation_error record = nil, fields: { }, base: [], status: :unprocessable_entity
    errors = { }

    if record
      record.errors.each do |error|
        errors[error.attribute] ||= []
        errors[error.attribute] << error.message
      end
    end

    fields.each do |attr, messages|
      errors[attr.to_sym] ||= []
      errors[attr.to_sym].concat(Array(messages))
    end

    base_errors = Array(base) + Array(errors.delete(:base))

    render json: { type: 'validation_error',
                   message: '入力内容を確認してください．',
                   errors:,
                   base_errors: },
           status:
  end

  def normalise_json value
    return nil if value.nil?
    return JSON.parse(value) if value.is_a?(String)

    value
  end
end
