import os

from charmhelpers.contrib.storage.linux.utils import (
    is_block_device,
    zap_disk,
)

from charmhelpers.contrib.storage.linux.loopback import (
    ensure_loopback_device,
)

from charmhelpers.contrib.storage.linux.lvm import (
    deactivate_lvm_volume_group,
    is_lvm_physical_volume,
    remove_lvm_physical_volume,
)

from charmhelpers.core.host import (
    mounts,
    umount,
    restart_on_change,
)

from charmhelpers.core.hookenv import (
    log,
    INFO,
    ERROR,
)

from charmhelpers.core.unitdata import (
    HookData,
    kv,
)

DEFAULT_LOOPBACK_SIZE = '5G'


def ensure_block_device(block_device):
    '''
    Confirm block_device, create as loopback if necessary.

    :param block_device: str: Full path of block device to ensure.

    :returns: str: Full path of ensured block device.
    '''
    if block_device.startswith("/"):
        # Resolve non-relative link(s) if device is a symlink to a real device.
        # This fixes/prevents bug #1577408.
        real_device = os.path.realpath(block_device)
        if real_device != block_device:
            log('Block device "{}" is a symlink to "{}". Using "{}".'.format(
                block_device, real_device, real_device), level=INFO)
            block_device = real_device

    _none = ['None', 'none', None]
    if (block_device in _none):
        log('prepare_storage(): Missing required input: '
            'block_device=%s.' % block_device, level=ERROR)
        raise

    if block_device.startswith('/dev/'):
        bdev = block_device
    elif block_device.startswith('/'):
        _bd = block_device.split('|')
        if len(_bd) == 2:
            bdev, size = _bd
        else:
            bdev = block_device
            size = DEFAULT_LOOPBACK_SIZE
        bdev = ensure_loopback_device(bdev, size)
    else:
        bdev = '/dev/%s' % block_device

    if not is_block_device(bdev):
        log('Failed to locate valid block device at %s' % bdev, level=ERROR)
        # ignore missing block devices
        return

    return bdev


def clean_storage(block_device):
    '''
    Ensures a block device is clean.  That is:
        - unmounted
        - any lvm volume groups are deactivated
        - any lvm physical device signatures removed
        - partition table wiped

    :param block_device: str: Full path to block device to clean.
    '''
    for mp, d in mounts():
        if d == block_device:
            log('clean_storage(): Found %s mounted @ %s, unmounting.' %
                (d, mp), level=INFO)
            umount(mp, persist=True)

    if is_lvm_physical_volume(block_device):
        deactivate_lvm_volume_group(block_device)
        remove_lvm_physical_volume(block_device)
    else:
        zap_disk(block_device)


def is_paused():
    """Is the unit paused?"""
    with HookData()():
        if kv().get('unit-paused'):
            return True
        else:
            return False


def pause_aware_restart_on_change(restart_map):
    """Avoids restarting services if config changes when unit is paused."""
    def wrapper(f):
        if is_paused():
            return f
        else:
            return restart_on_change(restart_map)(f)
    return wrapper