"""
KnackHQ Tests
"""
from unittest import mock

import pytest
from knackhq.knackhq import (
    KnackApp,
    KnackObject,
    KnackRecord,
    ObjectCollection,
    RecordCollection,
)
from knackhq.exceptions import (
    ApiResponseError,
    NotFoundError,
    ObjectNotFoundError,
)

KnackApp.APP_ID = 'app_id'
KnackApp.API_KEY = 'api_key'
KnackApp.ENDPOINT = 'https://localhost/v1'


def test_KnackApp_repr():
    app = KnackApp()
    assert repr(app) == 'KnackApp(https://localhost/v1)'


def test_RecordCollecion_repr():
    app = KnackApp()
    obj = KnackObject(app, {'key': 'object_1'})
    rds = RecordCollection(obj, {})
    assert repr(rds) == \
        'RecordCollection(https://localhost/v1/objects/object_1/records)'


@mock.patch('knackhq.KnackApp.get_objects')
def test_KnackApp_getitem(mock_obs):
    app = KnackApp()
    app['MyObject']
    mock_obs.return_value.__getitem__.assert_called_once_with('MyObject')


@mock.patch('knackhq.KnackApp.get_objects')
def test_KnackApp_iter(mock_obs):
    mock_obs.return_value = ['object_1', 'object_2', 'object_3']
    app = KnackApp()
    assert list(app) == ['object_1', 'object_2', 'object_3']


@mock.patch('requests.request')
def test_KnackApp_request(mock_req):
    app = KnackApp()
    app.request('GET', 'objects', 'object_1')
    mock_req.assert_called_once_with(
        'GET',
        'https://localhost/v1/objects/object_1',
        headers={'Content-Type': 'application/json',
                 'X-Knack-Application-Id': 'app_id',
                 'X-Knack-REST-API-Key': 'api_key'})


@mock.patch('requests.request')
def test_KnackApp_get_json(mock_req):
    mock_req.return_value.status_code = 200
    mock_req.return_value.json.return_value = {'fizz': 'buzz'}
    app = KnackApp()
    ret = app.get_json('object', 'object_1')
    assert ret == {'fizz': 'buzz'}


@mock.patch('requests.request')
def test_KnackApp_get_json_400(mock_req):
    mock_req.return_value.status_code = 400
    app = KnackApp()
    with pytest.raises(NotFoundError):
        app.get_json('object', 'object_1')


@mock.patch('requests.request')
def test_KnackApp_get_json_4XX(mock_req):
    mock_req.return_value.status_code = 429
    app = KnackApp()
    with pytest.raises(ApiResponseError):
        app.get_json('object', 'object_1')


@mock.patch('knackhq.KnackApp.get_json')
def test_KnackApp_get_objects(mock_json):
    mock_json.return_value = {'objects': [{'key': 'object_1',
                                           'name': 'MyObject'}]}
    app = KnackApp()
    ret = app.get_objects()
    assert ret.objects == [{'key': 'object_1', 'name': 'MyObject'}]


@mock.patch('knackhq.KnackApp.get_json')
def test_KnackApp_get_object(mock_json):
    mock_json.return_value = {'object': {'key': 'object_1',
                                         'name': 'MyObject'}}
    app = KnackApp()
    ret = app.get_object('object_1')
    assert ret.object == {'key': 'object_1', 'name': 'MyObject'}


@mock.patch('knackhq.KnackApp.get_json')
def test_KnackApp_get_object_err(mock_json):
    mock_json.return_value = {}
    app = KnackApp()
    with pytest.raises(ObjectNotFoundError):
        app.get_object('object_1')


@mock.patch('knackhq.KnackApp.get_object')
def test_KnackApp_get_records(mock_obj):
    app = KnackApp()
    app.get_records('object_1')
    mock_obj.assert_called_once_with('object_1')


def test_ObjectCollection_str():
    app = KnackApp()
    obs = ObjectCollection(app, [])
    assert str(obs) == 'https://localhost/v1/objects'


@mock.patch('knackhq.KnackApp.get_object')
def test_ObjectCollection_getitem_by_key(mock_obj):
    app = KnackApp()
    obs = ObjectCollection(app, [{'key': 'object_1', 'name': 'Object1'},
                                 {'key': 'object_2', 'name': 'Object2'}])
    obs['object_1']
    mock_obj.assert_called_once_with('object_1')


@mock.patch('knackhq.KnackApp.get_object')
def test_ObjectCollection_getitem_by_name(mock_obj):
    app = KnackApp()
    obs = ObjectCollection(app, [{'key': 'object_1', 'name': 'Object1'},
                                 {'key': 'object_2', 'name': 'Object2'}])
    obs['Object2']
    mock_obj.assert_called_once_with('object_2')


@mock.patch('knackhq.KnackApp.get_object')
def test_ObjectCollection_getitem_err(mock_obj):
    mock_obj.side_effect = ApiResponseError
    app = KnackApp()
    obs = ObjectCollection(app, [{'key': 'object_1', 'name': 'Object1'},
                                 {'key': 'object_2', 'name': 'Object2'}])
    with pytest.raises(ObjectNotFoundError):
        obs['Object3']


def test_ObjectCollection_iter():
    app = KnackApp()
    obs = ObjectCollection(app, [{'key': 'object_1', 'name': 'Object1'},
                                 {'key': 'object_2', 'name': 'Object2'}])
    assert list(obs) == ['object_1', 'object_2']


def test_KnackObject_iter():
    app = KnackApp()
    obj = KnackObject(app, {'key': 'object_1', 'name': 'Object1'})
    assert sorted(list(obj)) == ['key', 'name']


def test_KnackObject_get_records():
    app = KnackApp()
    obj = KnackObject(app, {'key': 'object_1', 'name': 'Object1'})
    rds = obj.get_records()
    assert rds.object == obj
    assert rds.query == {}


@mock.patch('knackhq.knackhq.KnackObject.get_records')
def test_KnackObject_get_record(mock_rds):
    app = KnackApp()
    obj = KnackObject(app, {'key': 'object_1', 'name': 'Object1'})
    rec = KnackRecord(obj, {})
    mock_rds.return_value = [rec]
    assert obj.get_record('record_id') == rec
