import argparse
from pathlib import Path
from pathlib import PurePosixPath
import tempfile
import sys
import signal

from backup_core import BackupJob, atomic_json, private_directory
from restore_drill import DatabaseDrill
from transport import ComposeStack, DockerRepository, OperationsError


def main():
    parser = argparse.ArgumentParser(description="Encrypted Post76 backup and file recovery; Docker uses only the local Unix socket")
    parser.add_argument("--project", required=True)
    parser.add_argument("--env-file", required=True)
    parser.add_argument("--work-dir", default=str(Path(__file__).resolve().parent / "workspace"))
    parser.add_argument("--compose-file", action="append", default=[])
    parser.add_argument("--local-repository")
    parser.add_argument("--include-legacy", action="store_true")
    subcommands = parser.add_subparsers(dest="command", required=True)
    subcommands.add_parser("init-repository")
    capture = subcommands.add_parser("backup")
    capture.add_argument("--quiesce", action="store_true")
    restore = subcommands.add_parser("restore-files")
    restore.add_argument("--snapshot", required=True)
    restore.add_argument("--destination", required=True)
    drill = subcommands.add_parser("restore-drill")
    drill.add_argument("--snapshot", required=True)
    drill.add_argument("--destination", required=True)
    subcommands.add_parser("recover-services")
    subcommands.add_parser("check-repository")
    subcommands.add_parser("list-snapshots")
    args = parser.parse_args()
    workspace = private_directory(args.work_dir)
    with tempfile.TemporaryDirectory(prefix="capture-", dir=workspace) as capture, \
            tempfile.TemporaryDirectory(prefix="restore-mount-", dir=workspace) as restore_mount:
        stack = ComposeStack(Path(__file__).resolve().parent.parent, args.project, args.env_file, workspace,
                             args.compose_file, args.local_repository, args.include_legacy)
        stack.mounts(capture, restore_mount)
        repository = DockerRepository(stack, args.include_legacy)
        job = BackupJob(stack, repository, workspace)
        with job.lock():
            stack.model()
            if args.command == "init-repository":
                repository.call("init")
                print("Encrypted repository initialized; keep its password separately for recovery")
            elif args.command == "backup":
                identity = job.backup(capture, args.quiesce)
                print("Verified backup snapshot: " + identity)
            elif args.command in ["restore-files", "restore-drill"]:
                target = job.restore(args.snapshot, args.destination)
                if args.command == "restore-drill":
                    metadata = repository.manifest(args.snapshot)
                    capture_root = PurePosixPath(metadata["roots"]["capture"])
                    sql_file = target / str(capture_root).lstrip("/") / "database.sql"
                    result = DatabaseDrill(workspace, metadata["mysql_image"]).run(sql_file, metadata["database"])
                    atomic_json(Path(args.destination) / "database-drill-result.json", result)
                print("Verified files restored into the new private directory: " + str(target))
            elif args.command == "recover-services":
                job.recover_services()
                print("Originally running application services resumed")
            elif args.command == "check-repository":
                repository.call("check", "--read-data")
                print("Encrypted repository data check passed")
            elif args.command == "list-snapshots":
                for snapshot in repository.snapshots(["post76", "format:1", "project:" + args.project, "verified"]):
                    print(snapshot["id"], snapshot["time"])


if __name__ == "__main__":
    def interrupted(signum, frame):
        raise KeyboardInterrupt()

    signal.signal(signal.SIGTERM, interrupted)
    try:
        main()
    except (OperationsError, ValueError, OSError) as error:
        print("Post76 operations failed: " + str(error), file=sys.stderr)
        raise SystemExit(1)
    except KeyboardInterrupt:
        print("Post76 operation interrupted; inspect maintenance status before another capture", file=sys.stderr)
        raise SystemExit(130)
