# pylint: disable=missing-docstring
import itertools

from rest_framework.test import APIRequestFactory, force_authenticate

from resolwe.flow.models import Data, Entity, Process
from resolwe.flow.views import DataViewSet
from resolwe.test import TestCase

factory = APIRequestFactory()  # pylint: disable=invalid-name


class ProjectionTest(TestCase):
    def setUp(self):
        super().setUp()

        self.entity = Entity.objects.create(name="Test entity", contributor=self.contributor)
        process = Process.objects.create(
            name="Test process",
            contributor=self.contributor,
            output_schema=[
                {'name': 'foo', 'label': 'Foo', 'group': [
                    {'name': 'bar', 'label': 'Bar', 'type': 'basic:integer:'},
                    {'name': 'hello', 'label': 'Hello', 'type': 'basic:string:'},
                ]},
                {'name': 'another', 'label': 'Another', 'type': 'basic:integer:'},
            ]
        )
        data_output = {
            'foo': {
                'bar': 42,
                'hello': 'world',
            },
            'another': 3,
        }
        self.data_output = data_output
        self.data = Data.objects.create(name="Test data", contributor=self.contributor, process=process,
                                        output=data_output)
        self.data_2 = Data.objects.create(name="Test data 2", contributor=self.contributor, process=process,
                                          output=data_output)

        self.entity.data.add(self.data)
        self.entity.data.add(self.data_2)

        self.data_viewset = DataViewSet.as_view(actions={'get': 'list'})

    def get_projection(self, fields):
        request = factory.get('/', {'fields': ','.join(fields)}, format='json')
        force_authenticate(request, self.admin)
        return self.data_viewset(request).data

    def test_projection(self):
        # Test top-level projection.
        all_fields = self.get_projection([])[0].keys()
        for field_count in range(1, 3):
            for fields in itertools.combinations(all_fields, field_count):
                data = self.get_projection(fields)[0]
                self.assertCountEqual(data.keys(), set(fields))

        # Test nested projection.
        data = self.get_projection(['entity__name,process__contributor__username'])[0]
        self.assertEqual(data, {
            'entity': {
                'name': 'Test entity'
            },
            'process': {
                'contributor': {
                    'username': 'contributor',
                },
            },
        })
        # Test deep projection: an empty projection means that all fields
        # should be returned without any projection.
        data = self.get_projection(['entity'])[0]
        self.assertCountEqual(data['entity'].keys(), [
            'created',
            'descriptor_dirty',
            'duplicated',
            'id',
            'modified',
            'contributor',
            'description',
            'descriptor',
            'descriptor_schema',
            'name',
            'settings',
            'slug',
            'tags',
            'collection',
            'descriptor_completed',
            'type',
        ])

        # Test top-level JSON projection.
        data = self.get_projection(['output'])[0]
        self.assertEqual(data['output'], self.data_output)

        # Test nested projection into JSON.
        data = self.get_projection(['output__foo__bar'])[0]
        self.assertEqual(data, {
            'output': {
                'foo': {
                    'bar': 42,
                },
            },
        })
