import subprocess
import os
import time
import sys
import shutil
import argparse


def run_command(command):
    """ Run a shell command and return its output """
    print(command)
    output = subprocess.check_output(command, shell=True).decode('utf-8').strip()
    print(output)
    return output


def stop_containers(containers):
    """Stop a list of containers."""
    container_list = ' '.join(containers)
    print(f"Stopping containers {container_list}...")
    run_command(f"docker stop {container_list}")


def start_containers(containers):
    """Start a list of containers."""
    container_list = ' '.join(containers)
    print(f"Starting containers {container_list}...")
    run_command(f"docker start {container_list}")


def is_database(image):
    databases = {"postgres", "mariadb", "redis", "memcached", "mongo"}
    prefix = image.split(':')[0]
    return prefix in databases


def is_symbolic_link(file_path):
    return os.path.islink(file_path)


def get_volume_path(volume):
    return run_command(f"docker volume inspect --format '{{{{ .Mountpoint }}}}' {volume}")


def get_image(container):
    return run_command(f"docker inspect --format='{{{{.Config.Image}}}}' {container}")


def has_healthcheck(container):
    """Check if a container has a HEALTHCHECK defined."""
    result = run_command(
        f"docker inspect --format='{{{{json .State.Health}}}}' {container}"
    )
    return result not in ("null", "")


def get_health_status(container):
    """Return the health status."""
    status = run_command(
        f"docker inspect --format='{{{{.State.Health.Status}}}}' {container}"
    )
    return status


def run_rsync(src, dest):
    run_command(f"rsync -aP --remove-source-files {src} {dest}")


def delete_directory(path):
    """Deletes a directory and all its contents."""
    try:
        shutil.rmtree(path)
        print(f"Directory {path} was successfully deleted.")
    except OSError as e:
        print(f"Error deleting directory {path}: {e}")


def pause_and_move(storage_path, volume, volume_path, containers):
    stop_containers(containers)
    storage_volume_path = os.path.join(storage_path, 'data', 'docker', 'volumes', volume)
    os.makedirs(storage_volume_path, exist_ok=False)
    run_rsync(f"{volume_path}/", f"{storage_volume_path}/")
    delete_directory(volume_path)
    os.symlink(storage_volume_path, volume_path)
    start_containers(containers)


def has_container_with_database(containers):
    for container in containers:
        image = get_image(container)
        if is_database(image):
            return True
    return False


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description='Migrate Docker volumes to SSD or HDD based on container image.'
    )
    parser.add_argument(
        '--rapid-storage-path', type=str, required=True,
        help='Path to the SSD storage'
    )
    parser.add_argument(
        '--mass-storage-path', type=str, required=True,
        help='Path to the HDD storage'
    )
    args = parser.parse_args()

    rapid_storage_path = args.rapid_storage_path
    mass_storage_path = args.mass_storage_path

    volumes = run_command("docker volume ls -q").splitlines()

    for volume in volumes:
        volume_path = get_volume_path(volume)
        containers = run_command(
            f"docker ps -q --filter volume={volume}"
        ).splitlines()

        if not containers:
            print(f"Skipped Volume {volume}. It does not belong to a running container.")
            continue
        if is_symbolic_link(volume_path):
            print(f"Skipped Volume {volume}. The storage path {volume_path} is a symbolic link.")
            continue

        # Wait until containers with a healthcheck are healthy (not starting or unhealthy)
        for container in containers:
            if has_healthcheck(container):
                status = get_health_status(container)
                while status != 'healthy':
                    print(f"Wait for Container {container}, Status '{status}'...")
                    time.sleep(1)
                    status = get_health_status(container)

        # Proceed with migration
        if has_container_with_database(containers):
            print(f"Safing volume {volume} on SSD.")
            pause_and_move(rapid_storage_path, volume, volume_path, containers)
        else:
            print(f"Safing volume {volume} on HDD.")
            pause_and_move(mass_storage_path, volume, volume_path, containers)

    print("Operation completed.")
