# -*- coding: utf-8 -*-
"""Defines fixtures available to all tests."""

import jwt
import pytest
from webtest import TestApp
from flask import Response, json
from flask.testing import FlaskClient
from werkzeug.datastructures import Headers

from api.app import create_app
from api.extensions import db, cache
from api.models.acl import User


class CMDBTestClient(FlaskClient):
    TEST_APP_SECRET = "test"

    def open(self, *args, **kwargs):
        headers = kwargs.pop("headers", Headers())
        headers.setdefault("User-Agent", "py.test")
        kwargs["headers"] = headers

        json_data = kwargs.pop("json", None)
        if json_data is not None:
            kwargs["data"] = json.dumps(json_data)
            if not kwargs.get("content_type"):
                kwargs["content_type"] = "application/json"

        auth = kwargs.pop("auth", (
            "Access-Token",
            jwt.encode({"sub": "test@xx.com"}, key=self.TEST_APP_SECRET)
        ))
        kwargs["headers"][auth[0]] = auth[1]

        return super(CMDBTestClient, self).open(*args, **kwargs)


class CMDBTestResponse(Response):
    @property
    def json(self):
        return json.loads(self.data)


@pytest.fixture(scope="session")
def app():
    """Create application for the tests."""
    _app = create_app("tests.settings")
    _app.config['SECRET_KEY'] = CMDBTestClient.TEST_APP_SECRET
    _app.test_client_class = CMDBTestClient
    _app.response_class = CMDBTestResponse

    ctx = _app.test_request_context()
    ctx.push()
    yield _app

    ctx.pop()


@pytest.fixture(scope="session")
def client(app):
    with app.test_client(use_cookies=False) as c:
        yield c


@pytest.fixture(scope="session")
def database(app):
    """Clean database after each case finished"""
    setup_db()
    yield db
    teardown_db()


@pytest.fixture(scope="function")
def session(database, app):
    with app.app_context():
        clean_db()
        clean_cache()
        yield database.session
        database.session.rollback()


def setup_db():
    teardown_db()
    db.create_all()
    # create test user


def teardown_db():
    db.session.remove()
    db.drop_all()
    db.session.bind.dispose()


def clean_cache():
    cache.clear()


def clean_db():
    """clean all data but not drop table"""
    for table in reversed(db.metadata.sorted_tables):
        if table.fullname in ["users"]:
            continue
        db.session.execute(table.delete())
        db.session.commit()

    if not User.get_by(email="test@xx.com"):
        u = User.create(
            flush=True,
            username="test",
            nickname="测试",
            email="test@xx.com",
            key="",
            secret=""
        )
        u._set_password("123456")
        u.save()


@pytest.fixture
def testapp(app):
    """Create Webtest app."""
    return TestApp(app)