from ldclient.base import BaseModel
from ldclient.enums import NightlyTaskType, InvalidateCacheRequestType


class AsyncRequest(BaseModel):
    """
    Base model for async requests.

    :type request_id: :class:`str`
    :param request_id: Unique identifier for the request.

    :type task_id: :class:`str`
    :param task_id: Unique identifier for the async task.
    """
    _fields = ['request_id', 'task_id']

    def __init__(self, request_id=None, task_id=None):
        self.request_id = request_id
        self.task_id = task_id


class ReplaceColumnAsyncRequest(AsyncRequest):
    """
    Represents async request for replacing a column.

    :type old_column_id: :class:`str`
    :param old_column_id: The ID of the column being swapped out.

    :type new_column_id: :class:`str`
    :param new_column_id: The ID of the column being swapped in.

    :type project_ids: :class:`list` of :class:`str`
    :param project_ids: List of (numeric) projects IDs in which to swap the columns.

    :type request_id: :class:`str`
    :param request_id: Unique identifier for the request.

    :type task_id: :class:`str`
    :param task_id: Unique identifier for the async task.
    """
    _fields = ['old_column_id', 'new_column_id', 'project_ids'] + AsyncRequest._fields

    def __init__(self,
                 old_column_id=None,
                 new_column_id=None,
                 project_ids=None,
                 request_id=None,
                 task_id=None):
        super(ReplaceColumnAsyncRequest, self).__init__(request_id=request_id, task_id=task_id)
        self.old_column_id = old_column_id
        self.new_column_id = new_column_id
        self.project_ids = project_ids


class NightlyTaskExecuteRequest(AsyncRequest):
    """
    Represents an async request for executing a nightly task.

    :type task_type: :class:`enums.NightlyTaskType`
    :param task_type: The type of nightly task to execute.

    :type request_id: :class:`str`
    :param request_id: Unique identifier for the request.

    :type task_id: :class:`str`
    :param task_id: Unique identifier for the async task.
    """
    _fields = ['task_type'] + AsyncRequest._fields

    def __init__(self, task_type=None, request_id=None, task_id=None):
        super(NightlyTaskExecuteRequest, self).__init__(request_id=request_id, task_id=task_id)
        self.task_type = task_type

    @staticmethod
    def deserialize_task_type(task_type):
        return NightlyTaskType.from_str(task_type)


class InvalidateLiveReportCacheRequest(BaseModel):
    """
    Represents an async request to invalidate the Live Report cache.

    :type request_type: :class:`enums.InvalidateCacheRequestType'
    :param request_type: Indicates how Live Reports will be invalidated - e.g. by ID, by entity, etc.

    :type observation_changes: :class:`list` of :class:`ChangedObservation`
    :param observation_changes: The observations by which to invalidate live reports. None unless invalidating by observation.

    :type live_report_changes: :class:`list` of :class:`ChangedLiveReport`
    :param live_report_changes: The IDs by which to invalidate live reports. None unless invalidating by ID.

    :type live_report_row_removals: :class:`list` of :class:`RemovedLiveReportRow`
    :param live_report_row_removals: The rows to remove & by which to invalidate live reports. None unless invalidating by removed row.

    :type entity_changes: :class:`list` of :class:`ChangedEntity`
    :param entity_changes: The entities by which to invalidate live reports. None unless invalidating by entity.
    """
    _fields = [
        'request_type', 'observation_changes', 'live_report_changes', 'live_report_row_removals',
        'entity_changes'
    ]

    def __init__(self,
                 request_type=None,
                 observation_changes=None,
                 live_report_changes=None,
                 live_report_row_removals=None,
                 entity_changes=None):
        self.request_type = request_type
        self.observation_changes = observation_changes
        self.live_report_changes = live_report_changes
        self.live_report_row_removals = live_report_row_removals
        self.entity_changes = entity_changes

    def serialize_observation_changes(self):
        return ChangedObservation.as_list(self.observation_changes)

    @staticmethod
    def deserialize_structure_attachments(observation_changes):
        return ChangedObservation.from_list(observation_changes)

    def serialize_live_report_changes(self):
        return ChangedLiveReport.as_list(self.live_report_changes)

    @staticmethod
    def deserialize_live_report_changes(live_report_changes):
        return ChangedLiveReport.from_list(live_report_changes)

    def serialize_live_report_row_removalss(self):
        return RemovedLiveReportRow.as_list(self.live_report_row_removals)

    @staticmethod
    def deserialize_live_report_row_removals(live_report_row_removals):
        return RemovedLiveReportRow.from_list(live_report_row_removals)

    def serialize_entity_changes(self):
        return ChangedEntity.as_list(self.entity_changes)

    @staticmethod
    def deserialize_entity_changes(entity_changes):
        return ChangedEntity.from_list(entity_changes)

    @staticmethod
    def deserialize_request_type(request_type):
        return InvalidateCacheRequestType.from_str(request_type)


class ChangedObservation(BaseModel):
    """ChangedObservation Class"""
    _fields = ['sample_id', 'protocol_id', 'type_id']

    def __init__(self, sample_id, protocol_id, type_id):
        super(ChangedObservation, self).__init__()
        self.type_id = type_id
        self.sample_id = sample_id
        self.protocol_id = protocol_id


class ChangedLiveReport(BaseModel):
    """ChangedLiveReport Class"""
    _fields = ['live_report_id']

    def __init__(self, live_report_id):
        super(ChangedLiveReport, self).__init__()
        self.live_report_id = live_report_id


class RemovedLiveReportRow(BaseModel):
    """
    RemovedLiveReportRow Class

    :type live_report_id: :class:`str`
    :param live_report_id: The numeric Live Report ID from which to remove the row

    :type entity_id: :class:`str`
    :param entity_id: The numeric entity ID of the row to remove
    """
    _fields = ['live_report_id', 'entity_id']

    def __init__(self, live_report_id, entity_id):
        super(RemovedLiveReportRow, self).__init__()
        self.live_report_id = live_report_id
        self.entity_id = entity_id


class ChangedEntity(BaseModel):
    """ChangedEntity Class"""
    _fields = ['entity_id']

    def __init__(self, entity_id):
        super(ChangedEntity, self).__init__()
        self.entity_id = entity_id
