from abc import ABC, abstractmethod

from notifications_utils.formatters import formatted_list

from app.formatters import format_thousands
from app.models import ModelList
from app.notify_client.service_api_client import service_api_client


class Event(ABC):
    def __init__(
        self,
        item,
        key=None,
        value_from=None,
        value_to=None,
    ):
        self.item = item
        self.time = item["updated_at"] or item["created_at"]
        self.user_id = item["created_by_id"]
        self.key = key
        self.value_from = value_from
        self.value_to = value_to

    @abstractmethod
    def __str__(self):
        pass

    @property
    @abstractmethod
    def relevant(self):
        pass


class ServiceCreationEvent(Event):
    relevant = True

    def __str__(self):
        return "Created this service and called it ‘{}’".format(self.item["name"])


class ServiceEvent(Event):
    @property
    def relevant(self):
        return self.value_from != self.value_to and bool(self._formatter)

    def __str__(self):
        return self._formatter()

    @property
    def _formatter(self):
        return getattr(self, "format_{}".format(self.key), None)

    def format_restricted(self):
        if self.value_to is False:
            return "Made this service live"
        if self.value_to is True:
            return "Put this service back into trial mode"

    def format_active(self):
        if self.value_to is False:
            return "Deleted this service"
        if self.value_to is True:
            return "Unsuspended this service"

    def format_contact_link(self):
        return "Set the contact details for this service to ‘{}’".format(self.value_to)

    def format_inbound_api(self):
        return "Updated the callback for received text messages"

    def format_message_limit(self):
        return ("{} this service’s daily message limit from {} to {}").format(
            "Reduced" if self.value_from > self.value_to else "Increased",
            format_thousands(self.value_from),
            format_thousands(self.value_to),
        )

    def format_name(self):
        return ("Renamed this service from ‘{}’ to ‘{}’").format(
            self.value_from, self.value_to
        )

    def format_permissions(self):
        added = list(sorted(set(self.value_to) - set(self.value_from)))
        removed = list(sorted(set(self.value_from) - set(self.value_to)))
        if removed and added:
            return "Removed {} from this service’s permissions, added {}".format(
                formatted_list(removed),
                formatted_list(added),
            )
        if added:
            return "Added {} to this service’s permissions".format(
                formatted_list(added)
            )
        if removed:
            return "Removed {} from this service’s permissions".format(
                formatted_list(removed)
            )

    def format_prefix_sms(self):
        if self.value_to is True:
            return "Set text messages to start with the name of this service"
        else:
            return "Set text messages to not start with the name of this service"

    def format_research_mode(self):
        if self.value_to is True:
            return "Put this service into research mode"
        else:
            return "Took this service out of research mode"

    def format_service_callback_api(self):
        return "Updated the callback for delivery receipts"


class APIKeyEvent(Event):
    relevant = True

    def __str__(self):
        if self.item["updated_at"]:
            return ("Revoked the ‘{}’ API key").format(self.item["name"])
        else:
            return ("Created an API key called ‘{}’").format(self.item["name"])


class APIKeyEvents(ModelList):
    model = APIKeyEvent
    client_method = service_api_client.get_service_api_key_history


class ServiceEvents(ModelList):
    client_method = service_api_client.get_service_service_history

    @property
    def model(self):
        return lambda x: x

    @staticmethod
    def splat(events):
        for index, item in enumerate(
            sorted(events, key=lambda event: event["updated_at"] or event["created_at"])
        ):
            if index == 0:
                yield ServiceCreationEvent(item)
            else:
                for key in sorted(item.keys()):
                    yield ServiceEvent(
                        item,
                        key,
                        events[index - 1][key],
                        events[index][key],
                    )

    def __init__(self, service_id):
        self.items = [
            event
            for event in self.splat(self.client_method(service_id))
            if event.relevant
        ]
