import { fromJS } from 'immutable'
import BaseRecord from '../baseRecord'
import { rule } from 'instructure-validations'

describe('BaseRecord', () => {
  class TestRecord extends BaseRecord({
    title: '',
    nestedValue: fromJS({
      here: ''
    })
  }) {
    static validations = {
      title: [rule('presence', {message: 'title cannot be blank'})],
      nestedValue: {
        here: [rule('presence', {message: 'here cannot be blank'})]
      }
    }
  }

  describe('.create', () => {
    class MockRecord1 extends BaseRecord({ id: '0' }) {}
    class MockRecord2 extends BaseRecord({ id: '0' }) {}

    it('it caches attributes previously seen', async () => {
      const attrs = fromJS({ id: '0' })
      const rec1 = MockRecord1.create(attrs)
      const rec2 = MockRecord1.create(attrs)
      expect(rec1 === rec2).to.be.true()
    })

    it('creates new records for new attributes', async () => {
      const rec1 = MockRecord1.create(fromJS({ id: '0' }))
      const rec2 = MockRecord1.create(fromJS({ id: '0' }))
      expect(rec1 === rec2).to.be.false()
    })

    it('caches each class separately', async () => {
      const attrs = fromJS({ id: '0' })
      const rec1 = MockRecord1.create(attrs)
      const rec2 = MockRecord2.create(attrs)
      expect(rec1 === rec2).to.be.false()
    })
  })

  describe('#getErrors', () => {
    const aTestRecord = new TestRecord()

    it('performs validations correctly', async () => {
      const expected = {
        title: ['title cannot be blank'],
        nestedValue: {
          here: ['here cannot be blank']
        }
      }

      expect(aTestRecord.getErrors().equals(fromJS(expected))).to.be.true()
    })


    it('takes an optional object to validate instead of `this`', async () => {
      const otherRecord = new TestRecord({title: 'a', nestedValue: {here: 'b'}})
      const expected = {
        title: ['title cannot be blank'],
        nestedValue: {
          here: ['here cannot be blank']
        }
      }

      expect(otherRecord.getErrors(aTestRecord).equals(fromJS(expected))).to.be.true()
    })
  })

  describe('#errorsIn', () => {
    it('gets the nested errors', async () => {
      const record = new TestRecord()
      expect(record.errorsIn(['nestedValue', 'here']).toJS()).to.eql(['here cannot be blank'])
    })

    it('returns a list by default', async () => {
      const record = new TestRecord()
      expect(record.errorsIn(['asdf'])).to.eq(fromJS([]))
    })
  })

  describe('errorsFor', () => {
    it('gets the error', async () => {
      const record = new TestRecord()
      expect(record.errorsFor('title').toJS()).to.eql(['title cannot be blank'])
    })
  })

  describe('isValid', () => {
    it('is true for empty errors', async () => {
      const record = new TestRecord({title: 'a', nestedValue: {here: 'b'}})
      expect(record.isValid()).to.be.true()
    })

    it('is false when errors are present', async () => {
      const record = new TestRecord({title: 'a', nestedValue: {here: ''}})
      expect(record.isValid()).to.be.false()
    })

    it('is true when nested errors are empty', async () => {
      const record = new TestRecord({title: 'a', nestedValue: {here: 'b'}})
      record.getErrors = () => fromJS({title: []})
      expect(record.isValid()).to.be.true()
    })
  })

  describe('validData', () => {
    it('gets the valid data', async () => {
      const record = new TestRecord({title: 'a'})
      expect(record.validData().toJS()).to.eql({title: 'a'})
    })
  })
})
