#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

"""Message Resource, Action, Detail and user visible message.

Use Resource, Action and Detail's combination to indicate the Event
in the format of:

EVENT: VOLUME_RESOURCE_ACTION_DETAIL

Also, use exception-to-detail mapping to decrease the workload of
classifying event in cinder's task code.
"""

from cinder.i18n import _


class Resource(object):

    VOLUME = 'VOLUME'
    VOLUME_SNAPSHOT = 'VOLUME_SNAPSHOT'
    VOLUME_BACKUP = 'VOLUME_BACKUP'


class Action(object):

    SCHEDULE_ALLOCATE_VOLUME = ('001', _('schedule allocate volume'))
    ATTACH_VOLUME = ('002', _('attach volume'))
    COPY_VOLUME_TO_IMAGE = ('003', _('copy volume to image'))
    UPDATE_ATTACHMENT = ('004', _('update attachment'))
    COPY_IMAGE_TO_VOLUME = ('005', _('copy image to volume'))
    UNMANAGE_VOLUME = ('006', _('unmanage volume'))
    EXTEND_VOLUME = ('007', _('extend volume'))
    CREATE_VOLUME_FROM_BACKEND = ('008',
                                  _('create volume from backend storage'))
    SNAPSHOT_CREATE = ('009', _('create snapshot'))
    SNAPSHOT_DELETE = ('010', _('delete snapshot'))
    SNAPSHOT_UPDATE = ('011', _('update snapshot'))
    SNAPSHOT_METADATA_UPDATE = ('012', _('update snapshot metadata'))
    BACKUP_CREATE = ('013', _('create backup'))
    BACKUP_DELETE = ('014', _('delete backup'))
    BACKUP_RESTORE = ('015', _('restore backup'))
    REIMAGE_VOLUME = ('016', _('reimage volume'))

    ALL = (SCHEDULE_ALLOCATE_VOLUME,
           ATTACH_VOLUME,
           COPY_VOLUME_TO_IMAGE,
           UPDATE_ATTACHMENT,
           COPY_IMAGE_TO_VOLUME,
           UNMANAGE_VOLUME,
           EXTEND_VOLUME,
           CREATE_VOLUME_FROM_BACKEND,
           SNAPSHOT_CREATE,
           SNAPSHOT_DELETE,
           SNAPSHOT_UPDATE,
           SNAPSHOT_METADATA_UPDATE,
           BACKUP_CREATE,
           BACKUP_DELETE,
           BACKUP_RESTORE,
           REIMAGE_VOLUME,
           )


class Detail(object):

    UNKNOWN_ERROR = ('001', _('An unknown error occurred.'))
    DRIVER_NOT_INITIALIZED = ('002',
                              _('Driver is not initialized at present.'))
    NO_BACKEND_AVAILABLE = ('003',
                            _('Could not find any available '
                              'weighted backend.'))
    FAILED_TO_UPLOAD_VOLUME = ('004',
                               _("Failed to upload volume to image "
                                 "at backend."))
    VOLUME_ATTACH_MODE_INVALID = ('005',
                                  _("Volume's attach mode is invalid."))
    QUOTA_EXCEED = ('006',
                    _("Not enough quota resource for operation."))
    NOT_ENOUGH_SPACE_FOR_IMAGE = ('007',
                                  _("Image used for creating volume exceeds "
                                    "available space."))
    UNMANAGE_ENC_NOT_SUPPORTED = (
        '008',
        _("Unmanaging encrypted volumes is not supported."))
    NOTIFY_COMPUTE_SERVICE_FAILED = (
        '009',
        _("Compute service failed to extend volume."))
    DRIVER_FAILED_EXTEND = (
        '010',
        _("Volume Driver failed to extend volume."))
    SIGNATURE_VERIFICATION_FAILED = (
        '011',
        _("Image signature verification failed."))
    DRIVER_FAILED_CREATE = (
        '012',
        _('Driver failed to create the volume.'))
    SNAPSHOT_CREATE_ERROR = ('013', _("Snapshot failed to create."))
    SNAPSHOT_UPDATE_METADATA_FAILED = (
        '014',
        _("Volume snapshot update metadata failed."))
    SNAPSHOT_IS_BUSY = ('015', _("Snapshot is busy."))
    SNAPSHOT_DELETE_ERROR = ('016', _("Snapshot failed to delete."))
    BACKUP_INVALID_STATE = ('017', _("Backup status is invalid."))
    BACKUP_SERVICE_DOWN = ('018', _("Backup service is down."))
    BACKUP_CREATE_DEVICE_ERROR = (
        '019', _("Failed to get backup device from the volume service."))
    BACKUP_CREATE_DRIVER_ERROR = (
        '020', ("Backup driver failed to create backup."))
    ATTACH_ERROR = ('021', _("Failed to attach volume."))
    DETACH_ERROR = ('022', _("Failed to detach volume."))
    BACKUP_CREATE_CLEANUP_ERROR = (
        '023', _("Cleanup of temporary volume/snapshot failed."))
    BACKUP_SCHEDULE_ERROR = (
        '024',
        ("Backup failed to schedule. Service not found for creating backup."))
    BACKUP_DELETE_DRIVER_ERROR = (
        '025', _("Backup driver failed to delete backup."))
    BACKUP_RESTORE_ERROR = (
        '026', _("Backup driver failed to restore backup."))
    VOLUME_INVALID_STATE = ('027', _("Volume status is invalid."))
    REIMAGE_VOLUME_FAILED = (
        '028',
        _("Compute service failed to reimage volume."))
    IMAGE_FORMAT_UNACCEPTABLE = (
        '029',
        _("The image disk format must be the same as the volume format for "
          "the volume type you are requesting."))
    INCREMENTAL_BACKUP_FORCES_FULL_BACKUP = (
        '030', _("Incremental backup not possible, forcing full backup."))

    ALL = (UNKNOWN_ERROR,
           DRIVER_NOT_INITIALIZED,
           NO_BACKEND_AVAILABLE,
           FAILED_TO_UPLOAD_VOLUME,
           VOLUME_ATTACH_MODE_INVALID,
           QUOTA_EXCEED,
           NOT_ENOUGH_SPACE_FOR_IMAGE,
           UNMANAGE_ENC_NOT_SUPPORTED,
           NOTIFY_COMPUTE_SERVICE_FAILED,
           DRIVER_FAILED_EXTEND,
           SIGNATURE_VERIFICATION_FAILED,
           DRIVER_FAILED_CREATE,
           SNAPSHOT_CREATE_ERROR,
           SNAPSHOT_UPDATE_METADATA_FAILED,
           SNAPSHOT_IS_BUSY,
           SNAPSHOT_DELETE_ERROR,
           BACKUP_INVALID_STATE,
           BACKUP_SERVICE_DOWN,
           BACKUP_CREATE_DEVICE_ERROR,
           BACKUP_CREATE_DRIVER_ERROR,
           ATTACH_ERROR,
           DETACH_ERROR,
           BACKUP_CREATE_CLEANUP_ERROR,
           BACKUP_SCHEDULE_ERROR,
           BACKUP_DELETE_DRIVER_ERROR,
           BACKUP_RESTORE_ERROR,
           VOLUME_INVALID_STATE,
           REIMAGE_VOLUME_FAILED,
           IMAGE_FORMAT_UNACCEPTABLE,
           INCREMENTAL_BACKUP_FORCES_FULL_BACKUP,
           )

    # Exception and detail mappings
    EXCEPTION_DETAIL_MAPPINGS = {
        DRIVER_NOT_INITIALIZED: ['DriverNotInitialized'],
        NO_BACKEND_AVAILABLE: ['NoValidBackend'],
        VOLUME_ATTACH_MODE_INVALID: ['InvalidVolumeAttachMode'],
        QUOTA_EXCEED: ['ImageLimitExceeded',
                       'BackupLimitExceeded',
                       'SnapshotLimitExceeded'],
        NOT_ENOUGH_SPACE_FOR_IMAGE: ['ImageTooBig'],
        SNAPSHOT_IS_BUSY: ['SnapshotIsBusy'],
    }


def translate_action(action_id):
    action_message = next((action[1] for action in Action.ALL
                           if action[0] == action_id), None)
    return action_message or 'unknown action'


def translate_detail(detail_id):
    detail_message = next((action[1] for action in Detail.ALL
                           if action[0] == detail_id), None)
    return detail_message or Detail.UNKNOWN_ERROR[1]


def translate_detail_id(exception, detail):
    """Get a detail_id to use for a message.

    If exception is in the EXCEPTION_DETAIL_MAPPINGS, returns the detail_id
    of the mapped Detail field.  If exception is not in the mapping or is None,
    returns the detail_id of the passed-in Detail field.  Otherwise, returns
    the detail_id of Detail.UNKNOWN_ERROR.

    :param exception: an Exception (or None)
    :param detail: a message_field.Detail field (or None)
    :returns: string
    :returns: the detail_id of a message_field.Detail field
    """
    if exception is not None and isinstance(exception, Exception):
        for key, value in Detail.EXCEPTION_DETAIL_MAPPINGS.items():
            if exception.__class__.__name__ in value:
                return key[0]
    if detail in Detail.ALL:
        return detail[0]
    return Detail.UNKNOWN_ERROR[0]
