import logging

from django.test.client import Client
from django.test.client import RequestFactory

from germanium import config
from germanium.tools import assert_http_redirect, capture_commit_callbacks

from .auth import AuthTestCaseMixin
from .default import GermaniumSimpleTestCase, GermaniumTestCase


class ClientTestCaseMixin(AuthTestCaseMixin):

    logger = logging.getLogger("tests")

    def setUp(self):
        self.c = Client()
        self.r_factory = RequestFactory()
        self.default_headers = {}
        super().setUp()

    def get_request_with_user(self, request):
        request.user = self.logged_user.user
        return request

    def logout(self):
        self.get(config.LOGOUT_URL)
        self.logged_user = None

    def authorize(self, username, password):
        assert_http_redirect(
            self.post(
                config.LOGIN_URL, {config.USERNAME: username, config.PASSWORD: password}
            )
        )

    def get(
        self,
        url,
        headers=None,
        execute_pre_commit=True,
        execute_on_commit=False,
        execute_on_commit_cascade=False,
    ):
        headers = headers or {}
        headers.update(self.default_headers)

        with capture_commit_callbacks(
            execute_pre_commit=execute_pre_commit,
            execute_on_commit=execute_on_commit,
            execute_on_commit_cascade=execute_on_commit_cascade,
        ):
            resp = self.c.get(url, **headers)
        return resp

    def put(
        self,
        url,
        data=None,
        headers=None,
        execute_pre_commit=True,
        execute_on_commit=False,
        execute_on_commit_cascade=False,
    ):
        headers = headers or {}
        data = data or {}
        headers.update(self.default_headers)

        if execute_on_commit:
            with capture_commit_callbacks(
                execute_pre_commit=execute_pre_commit,
                execute_on_commit=execute_on_commit,
                execute_on_commit_cascade=execute_on_commit_cascade,
            ):
                resp = self.c.put(url, data, **self.headers)
        else:
            resp = self.c.put(url, data, **self.headers)
        return resp

    def post(
        self,
        url,
        data,
        headers=None,
        execute_pre_commit=True,
        execute_on_commit=False,
        execute_on_commit_cascade=False,
    ):
        headers = headers or {}
        headers.update(self.default_headers)
        with capture_commit_callbacks(
            execute_pre_commit=execute_pre_commit,
            execute_on_commit=execute_on_commit,
            execute_on_commit_cascade=execute_on_commit_cascade,
        ):
            resp = self.c.post(url, data, **headers)
        return resp

    def head(
        self,
        url,
        headers=None,
        execute_pre_commit=True,
        execute_on_commit=False,
        execute_on_commit_cascade=False,
    ):
        headers = headers or {}
        headers.update(self.default_headers)

        with capture_commit_callbacks(
            execute_pre_commit=execute_pre_commit,
            execute_on_commit=execute_on_commit,
            execute_on_commit_cascade=execute_on_commit_cascade,
        ):
            resp = self.c.head(url, **headers)
        return resp

    def options(
        self,
        url,
        headers=None,
        execute_pre_commit=True,
        execute_on_commit=False,
        execute_on_commit_cascade=False,
    ):
        headers = headers or {}
        headers.update(self.default_headers)

        with capture_commit_callbacks(
            execute_pre_commit=execute_pre_commit,
            execute_on_commit=execute_on_commit,
            execute_on_commit_cascade=execute_on_commit_cascade,
        ):
            resp = self.c.options(url, **headers)
        return resp

    def delete(
        self,
        url,
        headers=None,
        execute_pre_commit=True,
        execute_on_commit=False,
        execute_on_commit_cascade=False,
    ):
        headers = headers or {}
        headers.update(self.default_headers)

        with capture_commit_callbacks(
            execute_pre_commit=execute_pre_commit,
            execute_on_commit=execute_on_commit,
            execute_on_commit_cascade=execute_on_commit_cascade,
        ):
            resp = self.c.delete(url, **headers)
        return resp


class ClientTestCase(ClientTestCaseMixin, GermaniumTestCase):
    pass


class SimpleClientTestCase(ClientTestCaseMixin, GermaniumSimpleTestCase):
    pass
