"""Basic unittests for the server resource control operations."""
from __future__ import absolute_import

from functools import partial

from six.moves import http_client
from django.contrib.auth.models import User
from django.test import Client, TransactionTestCase

from rotest.management.models import DemoComplexResourceData

from tests.api.utils import request


class TestLockResources(TransactionTestCase):
    """Assert operations of lock resources request."""
    fixtures = ['resource_ut.json']

    def setUp(self):
        """Setup test environment."""
        self.client = Client()
        _, token_object = request(client=self.client,
                                  path="tests/get_token", method="get")
        self.token = token_object.token
        self.requester = partial(request, self.client,
                                 "resources/lock_resources")

    def test_lock_empty(self):
        """Assert trying to lock no resources."""
        response, content = self.requester(
            json_data={
                "descriptors": [],
                "timeout": 0,
                "token": self.token
            })
        self.assertEqual(response.status_code, http_client.OK)
        self.assertEqual(len(content.resource_descriptors), 0)

    def test_lock_valid_resource(self):
        """Assert trying to lock valid resource."""
        response, content = self.requester(
            json_data={
                "descriptors": [
                    {
                        "type": "rotest.management.models.ut_models."
                                "DemoResourceData",
                        "properties": {}
                    }
                ],
                "timeout": 0,
                "token": self.token
            })
        self.assertEqual(response.status_code, http_client.OK)
        self.assertEqual(len(content.resource_descriptors), 1)

    def test_invalid_resource_field(self):
        """Assert invalid resource field filter requested."""
        response, _ = self.requester(
            json_data={
                "descriptors": [
                    {
                        "type": "rotest.management.models.ut_models."
                                "DemoResourceData",
                        "properties": {
                            "invalid_field": "field1"
                        }
                    }
                ],
                "timeout": 0,
                "token": self.token
            })
        self.assertEqual(response.status_code, http_client.BAD_REQUEST)

    def test_lock_complex(self):
        """Assert trying to lock complex resource."""
        resources = DemoComplexResourceData.objects.filter(
            name='complex_resource1')

        resource, = resources
        sub_resource = resource.demo1

        self.assertTrue(resource.is_available())
        self.assertTrue(sub_resource.is_available())
        response, _ = self.requester(
            json_data={
                "descriptors": [
                    {
                        "type": "rotest.management.models.ut_models."
                                "DemoComplexResourceData",
                        "properties": {}
                    }
                ],
                "timeout": 0,
                "token": self.token
            })
        # refresh from db
        resources = DemoComplexResourceData.objects.filter(
            name='complex_resource1')
        resource, = resources
        sub_resource = resource.demo1

        self.assertEqual(response.status_code, http_client.OK)
        self.assertFalse(resource.is_available())
        self.assertFalse(sub_resource.is_available())

    def test_lock_complex_sub_resource_unavailable(self):
        """Assert trying to lock resource with sub-resource unavailable."""
        resources = DemoComplexResourceData.objects.filter(
            name='complex_resource1')

        resource, = resources
        sub_resource = resource.demo1

        sub_resource.reserved = "unknown_person"
        sub_resource.save()

        response, _ = self.requester(
            json_data={
                "descriptors": [
                    {
                        "type": "rotest.management.models.ut_models."
                                "DemoComplexResourceData",
                        "properties": {}
                    }
                ],
                "timeout": 0,
                "token": self.token
            })

        resources = DemoComplexResourceData.objects.filter(
            name='complex_resource1')
        resource, = resources
        sub_resource = resource.demo1

        self.assertEqual(response.status_code, http_client.BAD_REQUEST)
        # no reserved nor owner for main resource
        self.assertFalse(resource.reserved)
        self.assertFalse(resource.owner)
        self.assertFalse(resource.is_available())

        # sub resource left untouched
        self.assertFalse(sub_resource.is_available())
        self.assertEqual(sub_resource.reserved, "unknown_person")


class TestLockResourcesInvalid(TransactionTestCase):
    """Assert operations of invalid lock resources requests."""

    def setUp(self):
        """Setup test environment."""
        self.client = Client()
        _, token_object = request(client=self.client,
                                  path="tests/get_token", method="get")
        self.token = token_object.token
        self.requester = partial(request, self.client,
                                 "resources/lock_resources")

    def test_invalid_input(self):
        """Assert invalid request."""
        # empty data
        response, _ = self.requester(json_data={})
        self.assertEqual(response.status_code,
                         http_client.INTERNAL_SERVER_ERROR)

    def test_no_user_in_db(self):
        """Assert locking user not in db."""
        # localhost is not in db
        response, content = self.requester(
            json_data={
                "descriptors": [],
                "timeout": 0,
                "token": self.token
            })
        self.assertEqual(response.status_code, http_client.BAD_REQUEST)
        self.assertEqual(content.details,
                         "User localhost has no matching object in the DB")

    def test_invalid_content_type(self):
        """Assert invalid request content type."""
        # invalid content type
        response, _ = self.requester(content_type="text/html")
        self.assertEqual(response.status_code,
                         http_client.INTERNAL_SERVER_ERROR)

    def test_invalid_resource(self):
        """Assert invalid resource requested."""
        User.objects.create_user(username='localhost',
                                 email='email@email.com',
                                 password='localhost')
        response, content = self.requester(
            json_data={
                "descriptors": [
                    {
                        "type": "invalidmodule.invalidtype",
                        "properties": {}
                    }
                ],
                "timeout": 0,
                "token": self.token
            })
        self.assertEqual(response.status_code, http_client.BAD_REQUEST)
        self.assertIn("Failed to extract type", content.details)
        self.assertIn("invalidmodule.invalidtype", content.details)
        self.assertIn("Reason: No module named", content.details)
        self.assertIn("invalidmodule", content.details)

        # no available resources
        response, content = self.requester(
            json_data={
                "descriptors": [
                    {
                        "type": "rotest.management.models.ut_models."
                                "DemoResourceData",
                        "properties": {}
                    }
                ],
                "timeout": 0,
                "token": self.token
            })

        self.assertEqual(response.status_code, http_client.BAD_REQUEST)
        self.assertTrue(content.details.startswith(
            "No existing resource meets the requirements"))
