import bleach

from newsreader.news.collection.constants import (
    WHITELISTED_ATTRIBUTES,
    WHITELISTED_TAGS,
)
from newsreader.news.core.models import Post


class Stream:
    """
    Contains the data and makes it available for processing
    """

    rule = None

    def __init__(self, rule):
        self.rule = rule

    def read(self):
        raise NotImplementedError

    def parse(self, response):
        raise NotImplementedError


class Client:
    """
    Retrieves the data through streams
    """

    stream = Stream

    def __init__(self, rules=[]):
        self.rules = rules

    def __enter__(self):
        for rule in self.rules:
            stream = self.stream(rule)

            yield stream.read()

    def __exit__(self, *args, **kwargs):
        pass


class Builder:
    """
    Builds instances of various types
    """

    instances = []
    stream = None
    payload = None

    def __init__(self, payload, stream):
        self.payload = payload
        self.stream = stream

    def __enter__(self):
        return self

    def __exit__(self, *args, **kwargs):
        pass

    def build(self):
        raise NotImplementedError

    def sanitize_fragment(self, fragment):
        if not fragment:
            return ""

        return bleach.clean(
            fragment,
            tags=WHITELISTED_TAGS,
            attributes=WHITELISTED_ATTRIBUTES,
            strip=True,
            strip_comments=True,
        )


class Collector:
    """
    Glue between client, streams and builder
    """

    client = None
    builder = None

    def __init__(self, client=None, builder=None):
        self.client = client if client else self.client
        self.builder = builder if builder else self.builder

    def collect(self, rules=None):
        raise NotImplementedError


class Scheduler:
    """
    Schedules rules according to certain ratelimitting
    """

    def get_scheduled_rules(self):
        raise NotImplementedError


class PostBuilder(Builder):
    rule_type = None

    def __enter__(self):
        self.existing_posts = {
            post.remote_identifier: post
            for post in Post.objects.filter(
                rule=self.stream.rule, rule__type=self.rule_type
            )
        }

        return super().__enter__()

    def save(self):
        for post in self.instances:
            post.save()


class PostStream(Stream):
    rule_type = None


class PostClient(Client):
    stream = PostStream

    def set_rule_error(self, rule, exception):
        length = rule._meta.get_field("error").max_length

        rule.error = exception.message[-length:]
        rule.succeeded = False


class PostCollector(Collector):
    def collect(self, rules=[]):
        with self.client(rules=rules) as client:
            for payload, stream in client:
                with self.builder(payload, stream) as builder:
                    builder.build()
                    builder.save()
