"""This file and its contents are licensed under the Apache License 2.0. Please see the included NOTICE for copyright information and LICENSE for a copy of the license.
"""
import json

import pytest
from django.conf import settings
from projects.functions.utils import recalculate_created_annotations_and_labels_from_scratch
from projects.models import Project, ProjectSummary

from ..utils import make_task, project_id  # noqa


def get_filtered_task_ids(business_client, view_id):
    response = business_client.get(f'/api/tasks/?view={view_id}')
    response_data = response.json()
    assert 'tasks' in response_data, response_data
    return [task['id'] for task in response_data['tasks']]


def apply_filter_and_get_view_id(business_client, project_id, filters):
    payload = {
        'project': project_id,
        'data': {'filters': filters},
    }
    response = business_client.post(
        '/api/dm/views/',
        data=json.dumps(payload),
        content_type='application/json',
    )
    assert response.status_code == 201, response.content
    return response.json()['id']


@pytest.mark.django_db
def test_views_filters_with_undefined(business_client, project_id):
    """
    1. Import task 1: {"$undefined$": "photo1.jpg"}
    2. Filter by `data` with value `photo`
    3. It should return task 1

    4. Set labeling config <View> <Image value="$image" name="img"/> </View>
    5. Filter by `image` with value `photo`
    6. It should return task 1

    7. Add task 2: {"$undefined$": "photo2.jpg", "extra": "123"}
    8. Filter by "extra": "123"
    9. It should return task 2
    10. Filter by "image" with value `photo`
    11. It should return task 1 and task 2

    12. Update task 1 with {"extra": "456"}
    13. Check project.summary.common_data_columns, there should be ["$undefined$", "extra"]

    14. Filter by "image" with "photo" should return task 1 and task 2
    """
    project = Project.objects.get(pk=project_id)
    project.label_config = '<View></View>'
    project.save()

    # Step 1: Import task 1: {"$undefined$": "photo1.jpg"}
    task_data_field_name = settings.DATA_UNDEFINED_NAME  # "$undefined$"
    task_1 = make_task({'data': {task_data_field_name: 'photo1.jpg'}}, project)
    task_id_1 = task_1.id

    # Step 2-3: Filter by `data` with value `photo`, should return task 1
    filters = {
        'conjunction': 'and',
        'items': [
            {
                # data default name when label config is not yet set
                # and a file is uploaded directly
                'filter': 'filter:tasks:data.data',
                'operator': 'contains',
                'type': 'String',
                'value': 'photo',
            }
        ],
    }
    view_id = apply_filter_and_get_view_id(business_client, project_id, filters)
    response_ids = get_filtered_task_ids(business_client, view_id)
    assert set(response_ids) == {task_id_1}, f'Expected {[task_id_1]}, got {response_ids}'

    # Step 4: Set labeling config <View> <Image value="$image" name="img"/> </View>
    project.label_config = '<View> <Image value="$image" name="img"/> </View>'
    project.save()

    # Step 5-6: Filter by `image` with value `photo`, should return task 1
    filters['items'][0]['filter'] = 'filter:tasks:data.image'
    view_id = apply_filter_and_get_view_id(business_client, project_id, filters)
    response_ids = get_filtered_task_ids(business_client, view_id)
    assert set(response_ids) == {task_id_1}, f'Expected {[task_id_1]}, got {response_ids}'

    # Step 7: Add task 2: {"$undefined$": "photo2.jpg", "extra": "123"}
    task_2 = make_task({'data': {task_data_field_name: 'photo2.jpg', 'extra': '123'}}, project)
    task_id_2 = task_2.id

    # Step 8-9: Filter by "extra": "123", should return task 2
    filters['items'][0]['filter'] = 'filter:tasks:data.extra'
    filters['items'][0]['value'] = '123'
    view_id = apply_filter_and_get_view_id(business_client, project_id, filters)
    response_ids = get_filtered_task_ids(business_client, view_id)
    assert set(response_ids) == {task_id_2}, f'Expected {[task_id_2]}, got {response_ids}'

    # Step 10-11: Filter by "image" with value `photo`, should return task 1 and task 2
    filters['items'][0]['filter'] = 'filter:tasks:data.image'
    filters['items'][0]['value'] = 'photo'
    view_id = apply_filter_and_get_view_id(business_client, project_id, filters)
    response_ids = get_filtered_task_ids(business_client, view_id)
    assert set(response_ids) == {task_id_1, task_id_2}, f'Expected {[task_id_1, task_id_2]}, got {response_ids}'

    # Step 12: Update task 1 with {"extra": "456"}
    task_1.data['extra'] = '456'
    task_1.save()

    # we need to fully reset cache, because summary.update_data_columns()
    # can't work incrementally
    recalculate_created_annotations_and_labels_from_scratch(project, project.summary, 1)

    # Step 13: Check project.summary.common_data_columns, there should be ["$undefined$", "extra"]
    project.refresh_from_db()
    summary = ProjectSummary.objects.get(project=project)
    assert set(summary.common_data_columns) == {
        task_data_field_name,
        'extra',
    }, f"Expected {[task_data_field_name, 'extra']}, got {summary.common_data_columns}"

    # Step 14: Filter by "image" with "photo" should return task 1 and task 2
    # The filter is already set to 'photo' for 'data.image' from previous steps
    view_id = apply_filter_and_get_view_id(business_client, project_id, filters)
    response_ids = get_filtered_task_ids(business_client, view_id)
    assert set(response_ids) == {task_id_1, task_id_2}, f'Expected {[task_id_1, task_id_2]}, got {response_ids}'
