import os
import base64
import pytest
import logging
from flask.testing import FlaskClient
from ..app import create_app
from ..config import APP_ROOT, get_config
from ..db import db
from .utils import (make_random_username, make_random_email, make_random_password,
                    make_user, make_datum_dict, make_datum, make_random_bio,
                    make_object_datum_dict, make_object_datum, make_test_dict,
                    make_test)


os.environ['FLASK_ENV'] = 'testing'
logger = logging.getLogger()


class JsonRespClient(FlaskClient):
    """More ergonomic test API client

    Basically traps calls to client.get, client.post, client.delete, and
    turns their JSON responses into actual python values, so we can assert
    based on responses easily. In case of a non-200 response, an exception is
    raised. If this behavior is not desired, pass raw=True.

    We achieve this by overriding the base 'open' method and inspecting the
    response.
    """
    def open(self, *args, **kwargs):
        raw = False
        if 'raw' in kwargs:
            raw = True
            del kwargs['raw']

        resp = super().open(*args, **kwargs)

        if not raw and kwargs['method'] in ['GET', 'POST', 'DELETE']:
            if resp.status != '200 OK':
                raise Exception('Got unexpected %s response for request. '
                                'Body was: "%s"' % (resp.status, resp.data))

            return resp.get_json()

        return resp


@pytest.fixture(scope='package')
def client():
    """Fixture to clear and set up the database and init an API client"""

    app = create_app()
    app.test_client_class = JsonRespClient
    with app.test_client() as c:
        with app.app_context():
            logger.info('Dropping all database tables')
            db.drop_all()
            logger.info('Recreating all database tables')
            db.create_all()
            yield c


@pytest.fixture(scope='function')
def username():
    return make_random_username()


@pytest.fixture(scope='function')
def email():
    return make_random_email()


@pytest.fixture(scope='function')
def password():
    return make_random_password()


@pytest.fixture(scope='function')
def bio():
    return make_random_bio()


@pytest.fixture(scope='function')
def user():
    return make_user()


@pytest.fixture(scope='function')
def other_user():
    return make_user()


@pytest.fixture(scope='function')
def admin_user():
    return make_user(is_admin=True)


@pytest.fixture(scope='function')
def datum_dict():
    return make_datum_dict()


@pytest.fixture(scope='function')
def datum():
    return make_datum()


@pytest.fixture(scope='function')
def object_datum_dict():
    return make_object_datum_dict()


@pytest.fixture(scope='function')
def object_datum():
    return make_object_datum()


@pytest.fixture(scope='package')
def avatar_png_b64():
    spidey_path = os.path.join(APP_ROOT, 'api', 'tests', 'fixtures', 'spidey.png')
    with open(spidey_path, 'rb') as spidey_img:
        return base64.b64encode(spidey_img.read()).decode('ascii')


@pytest.fixture(scope='package')
def avatar_post_b64():
    spidey_path = os.path.join(APP_ROOT, 'api', 'tests', 'fixtures', 'spidey_post.png')
    with open(spidey_path, 'rb') as spidey_img:
        return base64.b64encode(spidey_img.read()).decode('ascii')


@pytest.fixture(scope='package')
def config():
    return get_config()


@pytest.fixture(scope='function')
def test_dict():
    return make_test_dict()


@pytest.fixture(scope='function')
def test():
    return make_test()
