"""
This script check if a    device has dark datas
By default it display it and it can clean it
"""
import os
import sys
from oslo_config import cfg
from swift.common.ring import Ring
from socket import gethostname, gethostbyname
import shutil

# Initialize ring
try:
    ring = Ring('/etc/swift/object.ring.gz')
except Exception as e:
    sys.exit(e)

# Initialize script arguments
CLI_OPTS = [
    cfg.BoolOpt('clean-dark-parts', help='Delete dark partitions on disk'),
    cfg.BoolOpt('yes-i-really-mean-it', help='To really delete partitions'),
]


def _prepare_config():
    """
    Prepare the oslo config
    return: oslo config object
    """
    CONF = cfg.ConfigOpts()
    CONF.register_cli_opts(CLI_OPTS)
    return CONF


def get_ring_devs(ip):
    """
    Get device in ring for this server
    param ip: IP address of this server
    return: dict with device id and its name
    """
    devs = [d for d in ring.devs if d]
    devs_filter = [d for d in devs if d['ip'] == ip]
    if devs_filter:
        devs_ip = {d['id']: d['device'] for d in devs_filter}
    return devs_ip


def get_ring_parts(devs):
    """
    Get part in ring for a device
    param devs: list of devices in a server
    return: dict of list of partitions by device
    """
    partitions = {}
    for replica in ring._replica2part2dev_id:
        for partition, device in enumerate(replica):
            if device in devs:
                partitions.setdefault(devs[device], [])
                partitions[devs[device]].append(partition)
    return partitions


def get_dark_datas(ip, path, partitions):
    """
    Get dark datas on a device
    param ip: IP address of this server
    param path: path for search partitions on a Device
    params partitions: partitions on the ring for a device
    return: list of dark partitions
    """
    try:
        parts = [int(d) for d in os.listdir(path)
                 if os.path.isdir(os.path.join(path, d))]
    except Exception:
        print("Explore {path} -> Disk unmounted !", file=sys.stderr)
        return []
    diff = set(parts)-set(partitions)
    darks = list(diff)
    for dark in darks:
        for hand in ring.get_more_nodes(dark):
            if hand['handoff_index'] >= 3:
                break
            if ip == hand['ip']:
                darks.remove(dark)
                break
    return darks


def main():
    """
    Entry point for the script
    """
    CONF = _prepare_config()
    CONF(sys.argv[1:])
    if CONF.clean_dark_parts and not CONF.yes_i_really_mean_it:
        print('You should confirme delete by --yes-i-really-mean-it !',
              file=sys.stderr)
        return 1
    ip = gethostbyname(gethostname())
    devs = get_ring_devs(ip)
    parts = get_ring_parts(devs)
    for dev, parts in parts.items():
        path = os.path.join('/srv/node', dev, 'objects')
        darks = get_dark_datas(ip, path, parts)
        if CONF.clean_dark_parts and CONF.yes_i_really_mean_it:
            for dark in darks:
                dark_part = os.path.join(path, dark)
                print(f"Remove {dark_part} !")
                try:
                    shutil.rmtree(dark_part)
                except OSError as e:
                    print("Error: %s - %s." % (e.filename, e.strerror))
        elif darks:
            print('    ', dev, '>>>', ', '.join([str(d) for d in darks]))
    return 0


if __name__ == "__main__":
    sys.exit(main())
