# -*- coding: utf-8 -*-
#
# Iterativ GmbH
# http://www.iterativ.ch/
#
# Copyright (c) 2018 Iterativ GmbH. All rights reserved.
#
# Created on 25.09.18
# @author: Ramon Wenger <ramon.wenger@iterativ.ch>

import bleach
import re

from typing import List, Union

from wagtail.blocks import StreamValue

from api.utils import get_object
from assignments.models import Assignment
from books.models import ContentBlock


class AssignmentParameterException(Exception):
    pass


class ContentTypeException(Exception):
    pass


def get_previous_item(previous_contents: Union[StreamValue, List[dict]], item: dict):
    if isinstance(previous_contents, StreamValue):
        contents = previous_contents.raw_data
    else:
        contents = previous_contents
    return next((c for c in contents if c.get('id', None) == item['id']), None)


def handle_text(text):
    is_list = bool(re.search(r'<ul>', text))
    if is_list:
        # let's assume this is formatted correctly already, otherwise bleach or the browser should strip / ignore it
        return text
    else:
        parts = re.split(r'[\r\n]+', text)
        paragraphs = ['<p>{}</p>'.format(p.strip()) for p in parts]
        return '\n'.join(paragraphs)


ALLOWED_BLOCKS = (
    'text_block',
    'student_entry',
    'image_url_block',
    'link_block',
    'video_block',
    'assignment',
    'document_block',
    'content_list_item',
    'subtitle',
    'readonly'
)


def handle_content_block(content, context=None, module=None, allowed_blocks=ALLOWED_BLOCKS, previous_contents=None):
    """
    Handle different content types of a content block

    :param content: The current content block to be handled
    :param context: The request context of the current request, contains the user
    :param module: The module where this content block comes from, used for assignments
    :type module: books.models.Module
    :param allowed_blocks: List of allowed types of blocks
    :type allowed_blocks: list(str)
    :param previous_contents: The original content list from where this content block came from before being modified by the user
    """
    # todo: add all the content blocks
    # todo: sanitize user inputs!
    if content['type'] not in allowed_blocks:
        return

    if content['type'] == 'text_block':
        return {
            'type': 'text_block',
            'value': {
                'text': handle_text(bleach.clean(content['value']['text'], strip=True))
            }}
    elif content['type'] == 'assignment':
        if module is None:
            raise AssignmentParameterException('Module is missing for assignment')  # todo: define better exception
        if context is None:
            raise AssignmentParameterException('Context is missing for assignment')
        value = content['value']
        if value.get('id') is not None:
            assignment = get_object(Assignment, value.get('id'))
            if assignment.user_created and assignment.owner == context.user:
                assignment.title = value.get('title')
                assignment.assignment = value.get('assignment')
                assignment.save()
        else:
            assignment = Assignment.objects.create(
                title=value.get('title'),
                assignment=value.get('assignment'),
                owner=context.user,
                module=module,
                user_created=True
            )

        return {
            'type': 'assignment',
            'value': {
                'assignment_id': assignment.id
            }}
    elif content['type'] == 'image_url_block':
        return {
            'type': 'image_url_block',
            'value': {
                'url': bleach.clean(content['value']['url'])
            }}
    elif content['type'] == 'link_block':
        return {
            'type': 'link_block',
            'value': {
                'text': bleach.clean(content['value']['text']),
                'url': bleach.clean(content['value']['url'])
            }
        }
    elif content['type'] == 'video_block':
        return {
            'type': 'video_block',
            'value': {
                'url': bleach.clean(content['value']['url'])
            }}
    elif content['type'] == 'document_block':
        return {
            'type': 'document_block',
            'value': {
                'url': bleach.clean(content['value']['url'])
            }}
    elif content['type'] == 'subtitle':
        return {
            'type': 'subtitle',
            'value': {
                'text': bleach.clean(content['value']['text'])
            }
        }
    elif content['type'] == 'content_list_item':
        if content.get('id') is not None: # the list block existed already
            previous_item = get_previous_item(previous_contents=previous_contents, item=content)
            previous_content = previous_item.get('value')
        else:
            previous_content = None
        value = [handle_content_block(c, context, module, previous_contents=previous_content) for c in
                 content['contents']]
        content['value'] = value
        return content
    elif content['type'] == 'readonly' and previous_contents is not None:
        # get first item that matches the id
        # users can re-order readonly items, but we won't let them change them otherwise, so we just take the
        # item from before and ignore anything else
        previous_item = get_previous_item(previous_contents=previous_contents, item=content)
        if previous_item is None:
            raise ContentTypeException('Readonly item found that is not allowed here')
        return previous_item

    raise ContentTypeException('Type did not match a case')


def set_user_defined_block_type(block_type):
    if block_type == ContentBlock.TASK.upper():
        return ContentBlock.TASK
    else:
        return ContentBlock.NORMAL
