#!/usr/bin/env python3

import socket
import argparse
import sys
import json

sys.path.insert(0, '/usr/share/btrfs-snabak')
from snabak_modules.constants import *
from snabak_modules.commands import Command
from snabak_modules.config import Config, list_configs, list_snapshots


def terminate(code, message):
    print(message)
    sys.exit(code)


def call_daemon(command: Command):
    """
    This function will never return, it will call the daemon, wait for
    its answer, print it to stdout and then terminate the program.
    """
    try:
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        sock.connect(UNIX_SOCKET)
        sock.sendall(command.serialize() + NULL_BYTE)

        # wait for the answer
        reply_json = ''
        have_reply = False
        try:
            while True:
                rxchar = sock.recv(1).decode()

                if rxchar == "\x00":  # separator between two reply objects
                    reply = json.loads(reply_json)
                    print(reply["message"])
                    reply_json = ""
                    have_reply = True

                elif rxchar == "":  # socket was closed on the other end
                    sock.close()
                    if have_reply:
                        sys.exit(reply["exit_code"])  # noqa
                    else:
                        terminate(ERR_DAEMON, 'the daemon closed the socket unexpectedly')

                else:
                    reply_json += rxchar

        except (json.JSONDecodeError, KeyError):
            terminate(ERR_DAEMON, f"could not parse daemon reply: '{reply_json}'")

    except FileNotFoundError:
        terminate(ERR_DAEMON, f"could not find '{UNIX_SOCKET}', is the daemon not running?")

    except ConnectionRefusedError:
        terminate(ERR_DAEMON, "the socket is there but the daemon refuses to connect, did it crash?")

    except PermissionError:
        terminate(ERR_DAEMON, f"permission denied when trying to connect to socket '{UNIX_SOCKET}'")


def cmd_snapshot(args):
    if len(args.config) > 0:
        call_daemon(Command("sn", args.config))

    else:
        confs = '\n'.join(['  ' + c for c in list_configs()])
        terminate(
            ERR_USAGE,
            'Please specify at least one configuration name.\n' +
            'Available configurations are:\n' +
            confs
        )


def cmd_backup(_args):
    terminate(ERR_NOT_IMPLEMENTED, 'backup not yet implemented')


def cmd_list(_args):
    terminate(ERR_NOT_IMPLEMENTED, 'list not yet implemented')


def cmd_delete(args):
    call_daemon(Command("de", args.config, args.number))


def completion_assist(args):
    """
    Output the next possible completion words on the command line,
    given the previous two words. This function is invoked ONLY by the
    bash completion script that ships with this tool, it helps auto-
    completing commands, existing configurations and snapshot numbers.
    """
    commands = ['snapshot', 'backup', 'list', 'delete']
    commands2 = ['sn', 'ba', 'li', 'de']
    if args.prev == 'btrfs-snabak':
        print(' '.join(commands))

    elif args.prev2 in ['delete', 'de'] and args.prev in list_configs():
        try:
            conf = Config(args.prev)
            dirs = list_snapshots(conf)
            print(' '.join(dirs))
        except Exception:  # noqa
            print('invalid')

    elif args.prev in commands + commands2 + list_configs():
        print(' '.join(list_configs()))


if __name__ == '__main__':
    parser = argparse.ArgumentParser(epilog="help for individual commands: btrfs-snabak <command> --help")
    subparsers = parser.add_subparsers(dest='command', title='command')
    subparsers.required = True

    parser_sn = subparsers.add_parser('sn', aliases=['snapshot'], help='create snapshot')
    parser_sn.add_argument('config', nargs='*', help='configuration name')
    parser_sn.set_defaults(func=cmd_snapshot)

    parser_ba = subparsers.add_parser('ba', aliases=['backup'], help='create backup')
    parser_ba.add_argument('config', nargs='*', help='configuration name')
    parser_ba.set_defaults(func=cmd_backup)

    parser_li = subparsers.add_parser('li', aliases=['list'], help='list snapshots or backups')
    parser_li.add_argument('config', nargs='*', help='configuration name')
    parser_li.set_defaults(func=cmd_list)

    parser_de = subparsers.add_parser('de', aliases=['delete'], help='delete a snapshot')
    parser_de.add_argument('config', help='configuration name')
    parser_de.add_argument('number', help='snapshot number to delete')
    parser_de.set_defaults(func=cmd_delete)

    # this command is only used to assist the bash completion script
    parser_ca = subparsers.add_parser('compassist', help='only used by bash completion')
    parser_ca.add_argument('prev', help='previous word on the command line')
    parser_ca.add_argument('prev2', nargs='?', help='2nd previous word on the command line')
    parser_ca.set_defaults(func=completion_assist)

    arguments = parser.parse_args()
    arguments.func(arguments)
