#
# The PetsController defined here provides examples for different ways
# in which the 'returns' DSL directive can be used with the existing
# Apipie DSL elements 'param' and 'param_group'
#

class PetsController < ApplicationController
  resource_description do
    description 'A controller to test "returns"'
    short 'Pets'
    path '/pets'

    param :common_param, Integer, :desc => "A param that can optionally be passed to all Pet methods", :required => false

    returns :code => 404 do
      property :error_message, String, "description of the error"
    end

  end

  #-----------------------------------------------------------
  # simple 'returns' example: a method that returns a pet record
  #-----------------------------------------------------------
  api :GET, "/pets/:id/as_properties", "Get a pet record"
  returns :code => 200 do
    property :pet_name, String, :desc => "Name of pet", :required => false, :example => 'mypet'
    property :animal_type, %w[dog cat iguana kangaroo], :desc => "Type of pet"   # required by default, because this is a 'property'
  end
  returns :code => 404 do
    property :another_error_message, String, :desc => "Overriding the response description from the Pets resource"
  end
  def show_as_properties
    render :plain => "showing pet properties"
  end


  #-----------------------------------------------------------
  # same example as above, but this time the properties are defined
  # in a param group
  #-----------------------------------------------------------
  def_param_group :pet do
    property :pet_name, String, :desc => "Name of pet", :required => false
    property :animal_type, %w[dog cat iguana kangaroo], :desc => "Type of pet"   # required by default, because this is a 'property'
  end

  api :GET, "/pets/:id/as_param_group_of_properties", "Get a pet record"
  returns :pet, :code => 200, :desc => "The pet"
  def show_as_param_group_of_properties
    render :plain => "showing pet properties defined as param groups"
  end

  #-----------------------------------------------------------
  #  Method returning an array of the :pet param_group
  #-----------------------------------------------------------
  api :GET, "/pets", "Get all pets"
  returns :array_of => :pet, :desc => "list of pets"
  def index
    render :plain => "all pets"
  end


  #-----------------------------------------------------------
  # mixing request/response and response-only parameters
  #
  # the param_group :pet_with_id has several parameters which are
  # not expected in the request, but would show up in the response
  # and vice versa
  #-----------------------------------------------------------
  def_param_group :pet_with_id do
    param :pet_id, Integer, :desc => "id of pet", :required => true
    param :pet_name, String, :desc => "Name of pet", :required => false, :only_in => :response
    param :partial_match_allowed, [true, false], :desc => "Partial match allowed?", :required => false, :only_in => :request
    property :animal_type, %w[dog cat iguana kangaroo], :desc => "Type of pet"   # this is implicitly :only_in => :response
  end

  api :GET, "/pets/pet_by_id", "Get a pet record with the pet id in the body of the request"
  param_group :pet_with_id # only the pet_id is expected to be in here
  returns :param_group => :pet_with_id, :code => 200
  def show_pet_by_id
    render :plain => "returning a record with 3 fields"
  end



  #-----------------------------------------------------------
  # example with multiple param groups
  #-----------------------------------------------------------
  def_param_group :owner do   # a param group that can be used to describe inputs as well as outputs
    param :owner_name, String
  end
  def_param_group :user_response do # a param group that can be used to describe outputs only
    property :vote, [true,false]
  end

  api :GET, "/pets/by_owner_name/did_vote", "did any of the pets owned by the given user vote?"
  param_group :owner, :desc => "look up the user by name"
  returns :code => 200 do
    param_group :owner
    param_group :user_response
  end
  returns :code => 404  # no body
  def get_vote_by_owner_name
    render :plain => "no pets have voted"
  end


  #-----------------------------------------------------------
  # a method returning multiple codes,
  # some of which have a complex data structure
  #-----------------------------------------------------------
  def_param_group :pet_measurements do
    property :pet_measurements, Hash do
      property :weight, Integer, :desc => "Weight in pounds"
      property :height, Integer, :desc => "Height in inches"
      property :num_legs, Integer, :desc => "Number of legs", :required => false
    end
  end

  def_param_group :pet_history do
    property :did_visit_vet, [true,false], :desc => "Did the pet visit the veterinarian"
    property :avg_meals_per_day, Float, :desc => "Average number of meals per day"
  end

  api :GET, "/pets/:id/extra_info", "Get extra information about a pet"
  returns :code => 201, :desc => "Found a pet" do
    param_group :pet
  end
  returns :code => 202 do
    param_group :pet
    param_group :pet_measurements
  end
  returns :code => 203 do
    param_group :pet
    param_group :pet_measurements
    property 'pet_history', Hash do  # the pet_history param_group does not contain a wrapping object,
                                     # so create one manually
      param_group :pet_history
    end
    property 'additional_histories', :array_of => Hash do
      param_group :pet_history
    end
  end
  returns :code => 204 do
    property :int_array, :array_of => Integer
    property :enum_array, :array_of => %w[v1 v2 v3]
  end
  returns :code => :unprocessable_entity, :desc => "Fleas were discovered on the pet" do
    param_group :pet
    property :num_fleas, Integer, :desc => "Number of fleas on this pet"
  end
  def show_extra_info
    render :plain => "please disinfect your pet"
  end


  #=======================================================================
  # Methods for testing response validation
  #=======================================================================


  #-----------------------------------------------------------
  # A method which returns the response as described
  #-----------------------------------------------------------
  api!
  returns :code => 200 do
    property :a_number, Integer
    property :an_optional_number, Integer, :required=>false
  end
  def return_and_validate_expected_response
    result =  {
        a_number: 3
    }
    render :json => result
  end

  #-----------------------------------------------------------
  # A method which returns a null value in the response
  #-----------------------------------------------------------
  api!
  returns :code => 200 do
    property :a_number, Integer
    property :an_optional_number, Integer, :required=>false
  end
  def return_and_validate_expected_response_with_null
    result =  {
        a_number: nil
    }
    render :json => result
  end

  #-----------------------------------------------------------
  # A method which returns a null value in the response instead of an object
  #-----------------------------------------------------------
  api!
  returns :code => 200 do
    property :an_object, Hash do
      property :an_optional_number, Integer, :required=>false
    end
  end
  def return_and_validate_expected_response_with_null_object
    result =  {
        an_object: nil
    }
    render :json => result
  end

  #-----------------------------------------------------------
  # A method which returns an array response as described
  #-----------------------------------------------------------
  def_param_group :two_numbers do
    property :a_number, Integer
    property :an_optional_number, Integer, :required=>false
  end

  api!
  returns :code => 200, :array_of => :two_numbers
  def return_and_validate_expected_array_response
    result =  [{
                   a_number: 3
               }]
    render :json => result
  end

  #-----------------------------------------------------------
  # A method which returns an array response when it is expected to return an object
  # (note that response code is set here to 201)
  #-----------------------------------------------------------
  api!
  returns :two_numbers, :code => 201
  def return_and_validate_unexpected_array_response
    result =  [{
                   a_number: 3
               }]
    render :status => 201, :json => result
  end

  #-----------------------------------------------------------
  # A method which has a response that does not match the output type
  #-----------------------------------------------------------
  api!
  returns :code => 200 do
    property :a_number, String
  end
  def return_and_validate_type_mismatch
    result =  {
        a_number: 3
    }
    render :json => result
  end

  #-----------------------------------------------------------
  # A method with no documentation
  #-----------------------------------------------------------
  def undocumented_method
    render :json => {:result => "ok"}
  end

  #-----------------------------------------------------------
  # A method which has a response with a missing field
  #-----------------------------------------------------------
  api!
  returns :code => 200 do
    property :a_number, Integer
    property :another_number, Integer
  end
  def return_and_validate_missing_field
    result =  {
        a_number: 3
    }
    render :json => result
  end


  #-----------------------------------------------------------
  # A method which has a response with an extra property
  # (should raise a validation error by default)
  #-----------------------------------------------------------
  api!
  returns :code => 200 do
    property :a_number, Integer
  end
  def return_and_validate_extra_property
    result =  {
        a_number: 3,
        another_number: 4
    }
    render :json => result
  end


  #-----------------------------------------------------------
  # A method which has a response with an extra property, but 'additional_properties' is specified
  # (should not raise a validation error by default)
  #-----------------------------------------------------------
  api!
  returns :code => 200, :additional_properties => true do
    property :a_number, Integer
  end
  def return_and_validate_allowed_extra_property
    result =  {
        a_number: 3,
        another_number: 4
    }
    render :json => result
  end

  #-----------------------------------------------------------
  # Sub-object in response has an extra property. 'additional_properties' is specified on the response, but not on the object
  # (should raise a validation error by default)
  #-----------------------------------------------------------
  api!
  returns :code => 200, :additional_properties => true do
    property :an_object, Hash do
      property :a_number, Integer
    end
  end
  def sub_object_invalid_extra_property
    result =  {
        an_object: {
            a_number: 2,
            an_extra_number: 3
        }
    }
    render :json => result
  end


  #-----------------------------------------------------------
  # Sub-object in response has an extra property. 'additional_properties' is specified on the object, but not on the response
  # (should not raise a validation error by default)
  #-----------------------------------------------------------
  api!
  returns :code => 200, :additional_properties => false do
    property :an_object, Hash, :additional_properties => true do
      property :a_number, Integer
    end
  end
  def sub_object_allowed_extra_property
    result =  {
        an_object: {
            a_number: 2,
            an_extra_number: 3
        }
    }
    render :json => result
  end

  #=======================================================================
  # Methods for testing array field responses
  #=======================================================================

  #-----------------------------------------------------------
  # A method which returns the response as described (one field is an array of objects)
  #-----------------------------------------------------------
  api!
  returns :code => 200 do
    property :a_number, Integer
    property :array_of_objects, :array_of => Hash do
      property :number1, Integer, :required=>true
      property :number2, Integer, :required=>true
    end
  end
  def returns_response_with_valid_array
    result =  {
        a_number: 3,
        array_of_objects: [
          {number1: 1, number2: 2},
          {number1: 10, number2: 20}
        ]
    }
    render :json => result
  end


  #-----------------------------------------------------------
  # A method which returns an incorrect response (wrong field type inside array)
  #-----------------------------------------------------------
  api!
  returns :code => 200 do
    property :a_number, Integer
    property :array_of_objects, :array_of => Hash do
      property :number1, Integer, :required=>true
      property :number2, Integer, :required=>true
    end
  end
  def returns_response_with_invalid_array
    result =  {
        a_number: 3,
        array_of_objects: [
          {number1: 1, number2: 2},
          {number1: 10, number2: "this should have been a number"}
        ]
    }
    render :json => result
  end


  #-----------------------------------------------------------
  # A method with simple tags.
  #-----------------------------------------------------------
  api!
  tags(%w[Dogs Cats LivingBeings])
  def show_plain_response_with_tags
    render :plain => "showing pet properties"
  end

end

