require 'spec_helper'

################################################################################
# classes used in tests their naming is important because the respecting
# json schema is derived from it
################################################################################
class Client
  attr_accessor :first_name, :id, :addresses, :work_address, :created_at
end
class Address
  attr_accessor :city, :zip
end

class Contact
  attr_accessor :first_name, :last_name, :addresses, :id, :organisation
end

class OneOfDefinition
  attr_accessor :person
end

# see fixtures/lead.json
class Lead < Contact
  attr_accessor :links_clicked, :conversion
end

class Conversion
  attr_accessor :from, :to
end

class AnotherAddress
  include SchemaTools::Modules::Attributes
  has_schema_attrs 'address'
end

class NestedObjectNoProperties
 include SchemaTools::Modules::Attributes
 has_schema_attrs 'nested_object_no_properties'
end

describe SchemaTools::Hash do

  context 'from_schema to hash conversion' do

    let(:contact){Contact.new}
    let(:client){Client.new}

    before :each do
      contact.first_name = 'Peter'
      contact.last_name = 'Paul'
      contact.id = 'SomeID'

      client.created_at = Time.now
    end
    after :each do
      SchemaTools::Reader.registry_reset
    end

    it 'should return hash' do
      hash = SchemaTools::Hash.from_schema(contact)
      hash['last_name'].should == 'Paul'
    end

    it 'should format date-time fields to iso8601' do
      hash = SchemaTools::Hash.from_schema(client)
      hash['created_at'].should == client.created_at.iso8601
    end

    it 'keeps nil values' do
      hash = SchemaTools::Hash.from_schema(contact)
      hash.keys.should include('organisation')
      hash['organisation'].should be_nil
      # hash['birthday'].should be_nil
    end
    it 'skips unknown fields values' do
      hash = SchemaTools::Hash.from_schema(contact)
      # not defined in the Contact class above, but defined in the contact.json schema
      hash.keys.should_not include('birthday')
    end

    it 'should use custom reader' do
      reader = SchemaTools::Reader.new
      reader.read_all File.expand_path('../../fixtures/schemata', __FILE__)
      client = Client.new
      client.first_name = 'Egon'
      a1 = Address.new
      a1.city = 'Cologne'
      client.addresses = [a1]
      # use a object with nesting as this is problematic
      hash = SchemaTools::Hash.from_schema(client, reader: reader)
      hash['first_name'].should == 'Egon'
      hash['addresses'][0]['city'].should == 'Cologne'
    end

    it 'should use custom schema path' do
      custom_path = File.expand_path('../../fixtures/schemata', __FILE__)
      hash = SchemaTools::Hash.from_schema(contact, path: custom_path)
      hash['last_name'].should == 'Paul'
    end

    it 'should use custom schema' do
      hash = SchemaTools::Hash.from_schema(contact, class_name: :client)
      hash['last_name'].should == 'Paul'
    end

    it 'should use only give fields' do
      hash = SchemaTools::Hash.from_schema(contact, fields: ['id', 'last_name'])
      hash.keys.length.should == 2
      hash['last_name'].should == contact.last_name
      hash['id'].should == contact.id
      hash['first_name'].should be_nil
    end

    it 'has _links on object if exclude root' do
      hash = SchemaTools::Hash.from_schema(contact, links: true, class_name: :client)
      hash['_links'].length.should == 7
    end

  end

  context 'with nested values referencing a schema that is different from the class name' do
    let(:client){Client.new}

    it 'has an empty array if values are missing' do
      hash = SchemaTools::Hash.from_schema(client)
      hash['addresses'].should == []
    end

    it 'does not have the key if nested object is missing' do
      hash = SchemaTools::Hash.from_schema(client)
      hash.has_key?('work_address').should == false
    end

    it 'has nested array values' do
      a1 = AnotherAddress.new
      a1.city = 'Cologne'
      a1.zip = 50733
      client.addresses = [a1]
      hash = SchemaTools::Hash.from_schema(client)
      hash['addresses'].should == [{"id" => nil,
                                    "city"=>"Cologne",
                                    "address1" => nil,
                                    "zip"=>"50733",
                                    "country" => nil,
                                    "address_type" => nil}]
    end

    it 'has nested array values without root' do
      a1 = AnotherAddress.new
      a1.city = 'Cologne'
      a1.zip = 50733
      client.addresses = [a1]
      hash = SchemaTools::Hash.from_schema(client, exclude_root: true)
      hash['addresses'].should == [{"id" => nil,
                                    "city"=>"Cologne",
                                    "address1" => nil,
                                    "zip"=>"50733",
                                    "country" => nil,
                                    "address_type" => nil}]
    end

    it 'has nested object value' do
      a1 = AnotherAddress.new
      a1.city = 'Cologne'
      a1.zip = 50733
      client.work_address = a1
      hash = SchemaTools::Hash.from_schema(client)
      hash['work_address'].should == {"id" => nil,
                                      "city"=>"Cologne",
                                      "address1" => nil,
                                      "zip"=>"50733",
                                      "country" => nil,
                                      "address_type" => nil}
    end
  end

  context 'with nested values referencing a schema' do

    let(:client){Client.new}

    it 'has an empty array if values are missing' do
      hash = SchemaTools::Hash.from_schema(client)
      hash['addresses'].should == []
    end

    it 'does not have the key if nested object is missing' do
      hash = SchemaTools::Hash.from_schema(client)
      hash.has_key?('work_address').should == false
    end

    it 'has nested array values' do
      a1 = Address.new
      a1.city = 'Cologne'
      a1.zip = 50733
      client.addresses = [a1]
      hash = SchemaTools::Hash.from_schema(client)
      hash['addresses'].should == [{"city"=>"Cologne", "zip"=>"50733"}]
    end

    it 'has nested array values without root' do
      a1 = Address.new
      a1.city = 'Cologne'
      a1.zip = 50733
      client.addresses = [a1]
      hash = SchemaTools::Hash.from_schema(client, exclude_root: true)
      hash['addresses'].should == [{"city"=>"Cologne", "zip"=>"50733"}]
    end

    it 'has nested object value' do
      a1 = Address.new
      a1.city = 'Cologne'
      a1.zip = 50733
      client.work_address = a1
      hash = SchemaTools::Hash.from_schema(client)
      hash['work_address'].should == {"city"=>"Cologne", "zip"=>"50733"}
    end

    it 'has nested oneOf type object ' do
      contact = Contact.new
      contact.first_name = 'Pit'

      i = OneOfDefinition.new
      i.person = contact

      hash = SchemaTools::Hash.from_schema(i, exclude_root: true)
      hash['person']['first_name'].should == 'Pit'
    end

  end

  context 'with plain nested values' do

    let(:lead){Lead.new}
    before :each do
      lead.links_clicked = ['2012-12-12', '2012-12-15', '2012-12-16']
      conversion = Conversion.new
      conversion.from = 'whatever'
      conversion.to = 'whatever'
      lead.conversion = conversion
      @hash = SchemaTools::Hash.from_schema(lead)
    end
    after :each do
      SchemaTools::Reader.registry_reset
    end

    it 'should create array with values' do
      @hash['links_clicked'].should == lead.links_clicked
    end

    it 'should create object with values' do
      @hash['conversion']['from'].should == lead.conversion.from
      @hash['conversion']['to'].should == lead.conversion.to
    end

  end

  context 'with links' do
    let(:client){Client.new}
    before :each do
      client.first_name = 'Peter'
      client.id = 'SomeID'
    end
    after :each do
      SchemaTools::Reader.registry_reset
    end

    it 'has links' do
      hash = SchemaTools::Hash.from_schema(client, links: true)
      hash['_links'].length.should == 7
    end

    it 'should prepend base_url' do
      hash = SchemaTools::Hash.from_schema(client, base_url: 'http://json-hell.com', links: true)
      hash['_links'].first['href'].should include( 'http://json-hell.com')
    end

    it 'should replace placeholders' do
      client.id = 123
      hash = SchemaTools::Hash.from_schema(client, base_url: 'http://json-hell.com', links: true)
      hash['_links'].last['href'].should == 'http://json-hell.com/clients/123/Peter'
    end
  end

  context 'with a nested object that does not have properties' do
    let(:input_hash) { { foo: 'bar', baz: 'boom' } }
    let(:nested_object_no_properties) { NestedObjectNoProperties.from_hash(user_data: input_hash) }

    it 'should output the input hash' do
      hash = SchemaTools::Hash.from_schema(nested_object_no_properties)
      hash['user_data'].should == input_hash
    end
  end
end

