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


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:`~ldclient.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 LiveReport cache.

    :type request_type: :class:`~ldclient.enums.InvalidateCacheRequestType`
    :param request_type: Indicates how LiveReports 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 LiveReports. None unless invalidating by observation.

    :type live_report_changes: :class:`list` of :class:`ChangedLiveReport`
    :param live_report_changes: The IDs by which to invalidate LiveReports. 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 LiveReports. None unless invalidating by removed row.

    :type entity_changes: :class:`list` of :class:`ChangedEntity`
    :param entity_changes: The entities by which to invalidate LiveReports. 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 LiveReport 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


class ImportEntityAlias(BaseModel):
    """
    ImportEntityAlias class

    :type index: class:`int`
    :param index: The index of the entry

    :type id: class:`str`
    :param id: The alias associated with the entry

    :type update_if_empty: class:`bool`
    :param update_if_empty: This flag allows to update an existing entity with the specified file

    :type delete_model_results: class:`bool`
    :param delete_model_results: If the flag is set to true, the entity is updated with a new file & we delete all
           previous model results
    """
    _fields = ['index', 'id', 'update_if_empty', 'delete_model_results']

    def __init__(self,
                 index=None,
                 id=None,
                 update_if_empty=None,
                 delete_model_results=None):
        self.index = index
        self.id = id
        self.update_if_empty = update_if_empty
        self.delete_model_results = delete_model_results


class GenericEntityImportRequest(AsyncRequest):
    """
    GenericEntityImportRequest class

    :type request_id: :class:`str`
    :param request_id: The ID associated with the submitted request

    :type task_id: :class:`str`
    :param task_id: The ID associated with the submitted task

    :type type: :class:`~ldclient.enums.GenericEntityImportDataType`
    :param type: Specifies one of the acceptable types of imports

    :type live_report_id: :class:`str`
    :param live_report_id: LiveReport ID to import into

    :type entities: :class:`list` of :class:`~ldclient.requests.ImportEntityAlias`
    :param entities: Allows users to map rows to the corresponding IDs to be registered.
                         :meth:~ldclient.client.parse_csv_for_generic_entity_import can be used to parse IDs.


    :type sha1: :class:`str`
    :param sha1: hash of the uploaded file

    :type identifier: :class:`str`
    :param identifier: name of column in CSV that holds IDs

    :type import_entities: :class:`bool`
    :param import_entities: flag to determine if entities are to be imported

    :type import_properties: :class:`bool`
    :param import_properties: flag to determine if properties are to be imported

    :type observation_ids_to_delete: :class:`list` of :class:`str`
    :param observation_ids_to_delete: Ids of the observations to be deleted & should be obtained using
                                        :meth:~ldclient.client.get_observations_by_entity_ids_and_column_ids

    :type skipped_columns: :class:`list` of :class:`str`
    :param skipped_columns: names of the columns to be skipped

    :type entities_to_skipped_columns: :class:`dict`
    :param entities_to_skipped_columns: map of entity ID (str) to a list of column names (list[str]) to be skipped

    """
    _fields = [
        'request_id', 'task_id', 'type', 'live_report_id', 'entities', 'sha1',
        'identifier', 'import_entities', 'import_properties',
        'observation_ids_to_delete', 'skipped_columns',
        'entities_to_skipped_columns'
    ]

    def __init__(self,
                 request_id=None,
                 task_id=None,
                 type=None,
                 live_report_id=None,
                 entities=None,
                 sha1=None,
                 identifier=None,
                 import_entities=None,
                 import_properties=None,
                 observation_ids_to_delete=[],
                 skipped_columns=[],
                 entities_to_skipped_columns=None):
        super(AsyncRequest).__init__()
        self.request_id = request_id
        self.task_id = task_id
        self.type = type
        self.live_report_id = live_report_id
        self.entities = entities
        self.sha1 = sha1
        self.identifier = identifier
        self.import_entities = import_entities
        self.import_properties = import_properties
        self.observation_ids_to_delete = observation_ids_to_delete
        self.skipped_columns = skipped_columns
        self.entities_to_skipped_columns = entities_to_skipped_columns

    def serialize_entities(self):
        return ImportEntityAlias.as_list(self.entities)

    @staticmethod
    def deserialize_entities(entities):
        return ImportEntityAlias.from_list(entities)

    @staticmethod
    def deserialize_type(type):
        return GenericEntityImportDataType.from_str(type)
