from django.db import models
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.core.exceptions import ValidationError
from django.utils.html import strip_tags
import json

from openedx.core.djangoapps.xmodule_django.models import CourseKeyField


class Note(models.Model):
    user = models.ForeignKey(User, db_index=True)
    course_id = CourseKeyField(max_length=255, db_index=True)
    uri = models.CharField(max_length=255, db_index=True)
    text = models.TextField(default="")
    quote = models.TextField(default="")
    range_start = models.CharField(max_length=2048)  # xpath string
    range_start_offset = models.IntegerField()
    range_end = models.CharField(max_length=2048)  # xpath string
    range_end_offset = models.IntegerField()
    tags = models.TextField(default="")  # comma-separated string
    created = models.DateTimeField(auto_now_add=True, null=True, db_index=True)
    updated = models.DateTimeField(auto_now=True, db_index=True)

    class Meta:
        app_label = 'notes'

    def clean(self, json_body):
        """
        Cleans the note object or raises a ValidationError.
        """
        if json_body is None:
            raise ValidationError('Note must have a body.')

        body = json.loads(json_body)
        if not isinstance(body, dict):
            raise ValidationError('Note body must be a dictionary.')

        # NOTE: all three of these fields should be considered user input
        # and may be output back to the user, so we need to sanitize them.
        # These fields should only contain _plain text_.
        self.uri = strip_tags(body.get('uri', ''))
        self.text = strip_tags(body.get('text', ''))
        self.quote = strip_tags(body.get('quote', ''))

        ranges = body.get('ranges')
        if ranges is None or len(ranges) != 1:
            raise ValidationError('Note must contain exactly one range.')

        self.range_start = ranges[0]['start']
        self.range_start_offset = ranges[0]['startOffset']
        self.range_end = ranges[0]['end']
        self.range_end_offset = ranges[0]['endOffset']

        self.tags = ""
        tags = [strip_tags(tag) for tag in body.get('tags', [])]
        if len(tags) > 0:
            self.tags = ",".join(tags)

    def get_absolute_url(self):
        """
        Returns the absolute url for the note object.
        """
        # pylint: disable=no-member
        kwargs = {'course_id': self.course_id.to_deprecated_string(), 'note_id': str(self.pk)}
        return reverse('notes_api_note', kwargs=kwargs)

    def as_dict(self):
        """
        Returns the note object as a dictionary.
        """
        return {
            'id': self.pk,
            'user_id': self.user.pk,
            'uri': self.uri,
            'text': self.text,
            'quote': self.quote,
            'ranges': [{
                'start': self.range_start,
                'startOffset': self.range_start_offset,
                'end': self.range_end,
                'endOffset': self.range_end_offset
            }],
            'tags': self.tags.split(","),
            'created': str(self.created),
            'updated': str(self.updated)
        }
