import utils
from distribution import adapters
from distribution.models import get_model, ValidationError


class FeedSequencer(object):

    """
    In charge of making all necessary actions to keep the articles synced.

    Supported strategies: current|total|chunk
    """

    def __init__(self, request_data):
        self.items = request_data['items']
        self.domain = request_data['domain']
        try:
            # The default value feeditem is for backwards compatibility
            self.model = get_model(request_data.get('model', 'feeditem'))
        except KeyError:
            raise ValueError('Unknown model value')
        try:
            self.execute = getattr(self, request_data['sequence'])
        except AttributeError:
            raise ValueError('Unknown sequence value')

    def execute(self):
        raise NotImplemented('You need to set an strategy first.')

    def current(self):
        """
        Update all items inside the date range gave by the first publication_datetime
        and the current_datetime.
        """
        dates = sorted([utils.string_to_utc_date(element['publication_datetime'])
                        for element in self.items])

        from_date = utils.utc_date_to_string(dates[0])

        self.model.manager.purge(self.domain, from_date, 'NOW')
        return self.bulk_save(self.items, self.domain, self.model)

    def total(self):
        """
        Update all items for the domain.
        """
        self.model.manager.purge(self.domain, '*', '*')
        return self.bulk_save(self.items, self.domain, self.model)

    def chunk(self):
        """
        Update all items inside the date range gave by the first and
        last publication_datetime in the item_list.
        """
        dates = sorted([utils.string_to_utc_date(element['publication_datetime'])
                        for element in self.items])

        from_date = utils.utc_date_to_string(dates[0])
        to_date = utils.utc_date_to_string(dates[-1])

        self.model.manager.purge(self.domain, from_date, to_date)
        return self.bulk_save(self.items, self.domain, self.model)

    def delete(self):
        """
        Delete all items inside the item_list.
        Returns tuple (succeeded articles, errors).
        """
        error_ids, error_messages = self.model.manager.disable_many(self.items)
        correct = list(set(self.items) - set(error_ids))
        formatted_errors = []
        for error_idx in xrange(len(error_ids)):
            formatted_errors.append({
                'id': error_ids[error_idx],
                'domain': self.domain,
                'error': error_messages[error_idx]
            })

        adapters.run_delete(correct)

        return correct, formatted_errors

    def upsert(self):
        """
        Update an item if it already exists, or insert a new one if it doesn't
        """
        return self.bulk_save(self.items, self.domain, self.model)

    @staticmethod
    def _validate(items_data, domain, model, strict_validation=False):
        """
        Performs validation on items.
        Returns tuple (succeeded articles, errors).
        """
        items = []
        errors = []

        for item_data in items_data:
            try:
                items.append(model(item_data, domain, strict=strict_validation))
            except ValidationError as e:
                errors.append({
                    'id': item_data.get('id', 'N/A'),
                    'domain': domain,
                    'content_type': item_data.get('content_type', 'N/A'),
                    'uri': item_data.get('uri', None),
                    'error': e.message,
                })
        return items, errors

    def validate(self, strict_validation=False):
        """
        Validate items
        """
        items, errors = FeedSequencer._validate(self.items, self.domain, self.model,
                                                strict_validation=strict_validation)
        return [item.get('id') for item in items], errors

    def bulk_save(self, items_data, domain, model):
        """
        Performs bulk save operation.
        Returns tuple (succeeded articles, errors).
        """
        items, errors = FeedSequencer._validate(items_data, domain, model)

        model.manager.bulk_save(items, errors, domain)
        adapters.run_pre_renders(items)
        return [item.get('id') for item in items], errors
