import uuid


class CourseStructureAPIFixtureMixin(object):
    """Represents a course that can serialize itself in the form generated by the Course Structure API."""
    # pylint: disable=unused-argument
    def __init__(self, *args, **kwargs):
        self._uuid = uuid.uuid4().hex
        self._type = None
        self._display_name = kwargs.get('display_name', '')
        self.graded = False
        self._assignment_type = None
        self._children = []
        # Course data
        self.org = None
        self.course = None
        self.run = None

    def __repr__(self):
        return self.id

    def __getitem__(self, item):
        """
        Allows course structure fixture objects to be treated like the
        dict data structures they represent.
        """
        return self.to_dict().__getitem__(item)

    @property
    def children(self):
        return self._children

    def to_dict(self):
        """Return a dict representation of this block in the form generated by the Course Structure API."""
        return {
            'id': self.id,
            'type': self._type,
            'display_name': self._display_name,
            'graded': self.graded,
            'format': self._assignment_type,
            'children': [child.id for child in self._children]
        }

    def add_children(self, *children):
        """Add children to this block"""
        def _add_course_info(parent, child):
            """
            Children should inherit and cache org, course, and run for ID generation.
            'graded' is also inherited.
            """
            child.org = parent.org
            child.course = parent.course
            child.run = parent.run
            child.graded = parent.graded

        self._children += children
        self.pre_order(visit_fn=_add_course_info)
        return self

    def pre_order(self, visit_fn=None):
        """Return the fixture rooted at `self` as a list visited in pre-order."""
        visited = [self]
        for child in self._children:
            if visit_fn:
                visit_fn(self, child)
            visited += child.pre_order(visit_fn=visit_fn)
        return visited

    @property
    def id(self):
        """Uniquely identifies this block in the format used by the Course Structure API."""
        return 'i4x://{org}/{course}/{type}/{_uuid}'.format(
            org=self.org, course=self.course, type=self._type, _uuid=self._uuid
        )


class CourseFixture(CourseStructureAPIFixtureMixin):
    """Represents a course as returned by the Course Structure API."""
    def __init__(self, org='test_org', course='test_course', run='test_run', *args, **kwargs):
        super(CourseFixture, self).__init__(*args, **kwargs)
        self._type = 'course'
        self.org = org
        self.course = course
        self.run = run
        self._uuid = run  # The org/course/run triple uniquely identifies the course

    def course_structure(self):
        """Return a dict representing this course in the form generated by the Course Structure API."""
        return {
            'root': self.id,
            'blocks': {
                child.id: child.to_dict()
                for child in self.pre_order()
            }
        }


class ChapterFixture(CourseStructureAPIFixtureMixin):
    """Represents a chapter as returned by the Course Structure API."""
    def __init__(self, *args, **kwargs):
        super(ChapterFixture, self).__init__(*args, **kwargs)
        self._type = 'chapter'


class SequentialFixture(CourseStructureAPIFixtureMixin):
    """Represents a sequential as returned by the Course Structure API."""
    def __init__(self, graded=False, assignment_type=None, *args, **kwargs):
        super(SequentialFixture, self).__init__(*args, **kwargs)
        self.graded = graded
        self._assignment_type = assignment_type
        self._type = 'sequential'


class VerticalFixture(CourseStructureAPIFixtureMixin):
    """Represents a vertical as returned by the Course Structure API."""
    def __init__(self, *args, **kwargs):
        super(VerticalFixture, self).__init__(*args, **kwargs)
        self._type = 'vertical'


class VideoFixture(CourseStructureAPIFixtureMixin):
    """Represents a video as returned by the Course Structure API."""
    def __init__(self, *args, **kwargs):
        super(VideoFixture, self).__init__(*args, **kwargs)
        self._type = 'video'
