"""
Data Conversion utility methods for handling ORA2 XBlock data transformations and validation.

"""
import json


def convert_training_examples_list_to_dict(examples_list):
    """
    Convert of options selected we store in the problem def,
    which is ordered, to the unordered dictionary of options
    selected that the student training API expects.

    Args:
        examples_list (list): A list of options selected against a rubric.

    Returns:
        A dictionary of the given examples in the list.

    Example:
        >>> examples = [
        >>>     {
        >>>         "answer": {
        >>>             "parts": {
        >>>                 [
        >>>                     {"text:" "Answer part 1"},
        >>>                     {"text:" "Answer part 2"},
        >>>                     {"text:" "Answer part 3"}
        >>>                 ]
        >>>             }
        >>>         },
        >>>         "options_selected": [
        >>>             {
        >>>                 "criterion": "Ideas",
        >>>                 "option": "Fair"
        >>>             },
        >>>             {
        >>>                 "criterion": "Content",
        >>>                 "option": "Good"
        >>>             }
        >>>         ]
        >>>     }
        >>> ]
        >>> convert_training_examples_list_to_dict(examples)
        [
            {
                'answer': {
                    'parts': {
                        [
                            {'text:' 'Answer part 1'},
                            {'text:' 'Answer part 2'},
                            {'text:' 'Answer part 3'}
                        ]
                    }
                 },
                'options_selected': {
                    'Ideas': 'Fair',
                    'Content': 'Good'
                }
            }
        ]

    """
    return [
        {
            'answer': ex['answer'],
            'options_selected': {
                select_dict['criterion']: select_dict['option']
                for select_dict in ex['options_selected']
            }
        }
        for ex in examples_list
    ]


def list_to_conversational_format(str_list):
    """
    String list render method, displaying a list of string values in conversational language.
    ['a'] => 'a';  ['a', 'b'] => 'a and b';  ['a', 'b', 'c'] => 'a, b, and c'

    Args:
        str_list (str[]): List of strings to return in comma-joined/conversational form.

    Returns:
        Combined string.
    """
    if str_list is None:
        return ''
    if len(str_list) < 3:
        return ' and '.join(str_list)
    return '{}, and {}'.format(', '.join(str_list[:-1]), str_list[-1])


def update_saved_response_format(saved_response):
    """
    For backwards compatibility. Initially, problems had only one prompt and a
    string answer. This converts it to the appropriate shape.
    """

    updated_response_format = {}

    try:
        updated_response_format = {
            'answer': json.loads(saved_response),
        }
    except ValueError:
        updated_response_format = {
            'answer': {
                'text': saved_response,
            },
        }

    return updated_response_format


def update_assessments_format(assessments):
    """
    For each example update 'answer' to newer format.

    Args:
        assessments (list): list of assessments
    Returns:
        list of dict
    """
    for assessment in assessments:
        if 'examples' in assessment and assessment['examples']:
            for example in assessment['examples']:
                if isinstance(example, dict) and isinstance(example['answer'], str):
                    example['answer'] = {
                        'parts': [
                            {'text': example['answer']}
                        ]
                    }
                if isinstance(example, dict) and isinstance(example['answer'], list) and example['answer']:
                    example['answer'] = {
                        'parts': [
                            {'text': example_answer} for example_answer in example['answer']
                        ]
                    }
    return assessments


def create_prompts_list(prompt_or_serialized_prompts):
    """
    Construct a list of prompts.

    Initially a block had a single prompt which was saved as a simple string.
    In that case a new prompt dict is constructed from it.

    Args:
        prompt_or_serialized_prompts (unicode): A string which can either
        be a single prompt text or json for a list of prompts.

    Returns:
        list of dict
    """

    if prompt_or_serialized_prompts is None:
        prompt_or_serialized_prompts = ''

    try:
        prompts = json.loads(prompt_or_serialized_prompts)
    except ValueError:
        prompts = [
            {
                'description': prompt_or_serialized_prompts,
            }
        ]
    return prompts


def create_rubric_dict(prompts, criteria):
    """
    Construct a serialized rubric model in the format expected
    by the assessments app.

    Args:
        prompts (list of dict): The rubric prompts.
        criteria (list of dict): The serialized rubric criteria.

    Returns:
        dict

    """
    return {
        "prompts": prompts,
        "criteria": criteria
    }


def clean_criterion_feedback(rubric_criteria, criterion_feedback):
    """
    Remove per-criterion feedback for criteria with feedback disabled
    in the rubric.

    Args:
        rubric_criteria (list): The rubric criteria from the problem definition.
        criterion_feedback (dict): Mapping of criterion names to feedback text.

    Returns:
        dict

    """
    return {
        criterion['name']: criterion_feedback[criterion['name']]
        for criterion in rubric_criteria
        if criterion['name'] in criterion_feedback and criterion.get(
            'feedback', 'disabled'
        ) in ['optional', 'required']
    }


def prepare_submission_for_serialization(submission_data):
    """
    Convert a list of answers into the right format dict for serialization.

    Args:
        submission_data (list of unicode): The answers.

    Returns:
        dict
    """
    return {
        'parts': [{'text': text} for text in submission_data],
    }


def format_files_for_submission(uploaded_files):
    """
    Format a list of FileDescriptors as separate lists of file metadata.

    Args:
    * uploaded_files (List FileDescriptors)

    Returns:
    {
        "file_keys": (List of URLs)
        "files_descriptions": (List of strings)
        "files_names": (List of strings)
        "files_sizes": (List of ints / bytes)
    }
    """

    files_metadata = {}

    for field in ('file_keys', 'files_descriptions', 'files_names', 'files_sizes'):
        files_metadata[field] = []

    for upload in uploaded_files:
        files_metadata['file_keys'].append(upload.key)
        files_metadata['files_descriptions'].append(upload.description)
        files_metadata['files_names'].append(upload.name)
        files_metadata['files_sizes'].append(upload.size)

    return files_metadata


def create_submission_dict(submission, prompts):
    """
    1. Convert from legacy format.
    2. Add prompts to submission['answer']['parts'] to simplify iteration in the template.

    Args:
        submission (dict): Submission dictionary.
        prompts (list of dict): The prompts from the problem definition.

    Returns:
        dict
    """
    parts = [{'prompt': prompt, 'text': ''} for prompt in prompts]

    if 'text' in submission['answer']:
        parts[0]['text'] = submission['answer'].pop('text')
    else:
        for index, part in enumerate(submission['answer'].pop('parts')):
            parts[index]['text'] = part['text']

    submission['answer']['parts'] = parts

    return submission


def make_django_template_key(key):
    """
    Django templates access dictionary items using dot notation,
    which means that dictionary keys with hyphens don't work.
    This function sanitizes a key for use in Django templates
    by replacing hyphens with underscores.

    Args:
        key (basestring): The key to sanitize.

    Returns:
        basestring
    """
    return key.replace('-', '_')


def verify_assessment_parameters(func):
    """
    Verify that the wrapped function receives the given parameters.

    Used for the staff_assess, self_assess, peer_assess functions and uses their data types.

    Args:
        func - the function to be modified

    Returns:
        the modified function
    """
    def verify_and_call(instance, data, suffix):
        """ Inner Method. """
        # Validate the request
        if 'options_selected' not in data:
            return {'success': False, 'msg': instance._('You must provide options selected in the assessment.')}

        if 'overall_feedback' not in data:
            return {'success': False, 'msg': instance._('You must provide overall feedback in the assessment.')}

        if 'criterion_feedback' not in data:
            return {'success': False, 'msg': instance._('You must provide feedback for criteria in the assessment.')}

        return func(instance, data, suffix)
    return verify_and_call
