import sys
import hashlib
import json
from django.template.loader import render_to_string
from django.contrib.staticfiles import finders
from django.utils.safestring import mark_safe
from django.utils import six
from django_react.settings import REACT_EXTERNAL
from .exceptions import (
    ReactComponentCalledDirectly, ReactComponentMissingSource, PropSerializationError, ComponentBundlingError,
)
from .render import render_component
from .bundles import ReactBundle


class ReactComponent(object):
    source = None
    path_to_source = None
    url_to_source = None
    props = None
    serialized_props = None
    variable = None
    props_variable = None
    bundle = ReactBundle

    def __init__(self, **kwargs):
        # As we use the subclass's name to generate a number of client-side
        # variables, we disallow directly calling the ReactComponent class
        if self.__class__ is ReactComponent:
            raise ReactComponentCalledDirectly('Components must inherit from ReactComponent')
        # Ensure that a source file is defined in either `source` or `path_to_source`
        if self.get_path_to_source() is None:
            raise ReactComponentMissingSource(self)
        # All kwargs are passed to the component as props, this replicates
        # the API used in React+JSX
        self.props = kwargs

    def render_to_string(self, wrap=None):
        """
        Render a component to its initial HTML. You can use this method to generate HTML
        on the server and send the markup down on the initial request for faster page loads
        and to allow search engines to crawl you pages for SEO purposes.

        `render_to_string` takes an optional argument `wrap` which, if set to False, will
        prevent the the rendered output from being wrapped in the container element
        generated by the component's `render_container` method.

        Note that the rendered HTML will be wrapped in a container element generated by the component's
        `render_container` method.

        ```
        {{ component.render_to_string }}
        ```
        """
        rendered = render_component(
            path_to_source=self.get_path_to_source(),
            serialized_props=self.get_serialized_props(),
        )
        if wrap is None:
            return self.render_container(
                content=mark_safe(rendered)