#!/usr/bin/env python
#
# This file is part of vospace-transfer-service
# Copyright (C) 2021 Istituto Nazionale di Astrofisica
# SPDX-License-Identifier: GPL-3.0-or-later
#

import re
import sys

from redis_rpc_client import RedisRPCClient
from config import Config
from tabulate import tabulate


class VOSUser(RedisRPCClient):

    def __init__(self):
        config = Config("/etc/vos_cli/vos_cli.conf")
        params = config.loadSection("server")
        self.host = params["host"]
        self.port = params.getint("port")
        self.db = params.getint("db")
        params = config.loadSection("vos_user")
        self.rpcQueue = params["rpc_queue"]
        super(VOSUser, self).__init__(self.host, self.port, self.db, self.rpcQueue)

    def add(self):
        userId = None
        username = None
        email = None
        while not userId:
            try:
                userId = input("\nUser ID: ")
            except ValueError:
                print("Input type is not valid!")
            except EOFError:
                print("\nPlease, use CTRL+C to quit.")
            except KeyboardInterrupt:
                sys.exit("\nCTRL+C detected. Exiting...")
        while not username:
            try:
                username = input("\nUsername: ")
            except ValueError:
                print("Input type is not valid!")
            except EOFError:
                print("\nPlease, use CTRL+C to quit.")
            except KeyboardInterrupt:
                sys.exit("\nCTRL+C detected. Exiting...")
        while not email:
            try:
                email = input("\nE-mail: ")
                pattern = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"
                if not re.fullmatch(pattern, email):
                    print("Invalid e-mail address!")
                    email = None
                    continue
            except ValueError:
                print("Input type is not valid!")
            except EOFError:
                print("\nPlease, use CTRL+C to quit.")
            except KeyboardInterrupt:
                sys.exit("\nCTRL+C detected. Exiting...")
                
        userRequest = { "requestType": "USER_ADD",
                        "userId": userId,
                        "username": username,
                        "email": email }
        userResponse = self.call(userRequest)

        if "responseType" not in userResponse:
            sys.exit("FATAL: Malformed response, acknowledge expected.\n")
        elif userResponse["responseType"] == "USER_ADD_DONE":
            print("\nUser added successfully!\n")
        elif userResponse["responseType"] == "ERROR":
            errorCode = userResponse["errorCode"]
            errorMsg = userResponse["errorMsg"]
            sys.exit(f"\nError code: {errorCode}\nError message: {errorMsg}\n")
        else:
            sys.exit("\nFATAL: Unknown response type.\n")
            
    def delete(self):
        userRequest = { "requestType": "USER_DEL_REQ" }
        userResponse = self.call(userRequest)

        if "responseType" not in userResponse:
            sys.exit("FATAL: Malformed response, acknowledge expected.\n")
        elif userResponse["responseType"] == "USER_DEL_ACK":
            userList = userResponse["userList"]
            if not userList:
                sys.exit("\nNo username found.\n")
            userIdList = []
            for user in userList:
                userIdList.append(user["user_id"])
            userId = None
            while not userId in userIdList:
                try:
                    userId = input("\nPlease, insert a valid user ID: ")
                except ValueError:
                    print("Input type is not valid!")
                except EOFError:
                    print("\nPlease, use CTRL+C to quit.")
                except KeyboardInterrupt:
                    sys.exit("\nCTRL+C detected. Exiting...")
            print("\n!!!!!!!!!!!!!!!!!!!!!!!!WARNING!!!!!!!!!!!!!!!!!!!!!!!!")
            print("! This operation will remove the selected user only   !")
            print("! from the database.                                  !")
            print("! The user on the transfer node will not be removed,  !")
            print("! you must do it manually.                            !")
            print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n")
            confirm = None
            while not confirm in ( "yes", "no" ):
                try:
                    confirm = input("Are you sure to proceed? [yes/no]: ")
                except KeyboardInterrupt:
                    sys.exit("\nCTRL+C detected. Exiting...")
                except EOFError:
                    print("\nPlease, use CTRL+C to quit.")
            if confirm == "yes":
                confirmRequest = { "requestType": "USER_DEL_CON", "userId": userId }
                confirmResponse = self.call(confirmRequest)
                if "responseType" not in confirmResponse:
                    sys.exit("\nFATAL: Malformed response, confirmation expected.\n")
                elif confirmResponse["responseType"] == "USER_DEL_DONE":
                    print("\nUser deleted successfully!\n")
                elif confirmResponse["responseType"] == "ERROR":
                    errorCode = confirmResponse["errorCode"]
                    errorMsg = confirmResponse["errorMsg"]
                    sys.exit(f"\nError code: {errorCode}\nError message: {errorMsg}\n")
                else:
                    sys.exit("\nFATAL: Unknown response type.\n")
        elif userResponse["responseType"] == "ERROR":
            errorCode = userResponse["errorCode"]
            errorMsg = userResponse["errorMsg"]
            sys.exit(f"Error code: {errorCode}\nError message: {errorMsg}\n")
        else:
            sys.exit("\nFATAL: Unknown response type.\n")

    def search(self, searchStr):
        userRequest = { "requestType": "USER_SEARCH", "searchStr": searchStr }
        userResponse = self.call(userRequest)
        if "responseType" not in userResponse:
            sys.exit("FATAL: Malformed response.\n")
        elif userResponse["responseType"] == "SEARCH_DONE":
            userSearch = userResponse["userSearch"]
            if userSearch:
                print("\n" + tabulate(userResponse["userSearch"], headers = "keys", tablefmt = "pretty") + "\n")
            else:
                sys.exit(f"\nThe search did not return any results.\n")
        elif userResponse["responseType"] == "ERROR":
            errorCode = userResponse["errorCode"]
            errorMsg = userResponse["errorMsg"]
            sys.exit(f"\nError code: {errorCode}\nError message: {errorMsg}\n")
        else:
            sys.exit("\nFATAL: Unknown response type.\n")
    
    def help(self):
        sys.exit("""
NAME
       vos_user

SYNOPSYS
       vos_user COMMAND [ARGUMENT]

DESCRIPTION
       Client tool to manage VOSpace users in the database.

       The client accepts only one (mandatory) command at a time.
       A list of supported commands is shown here below:

       add
           adds a user to the database

       del
           deletes a user from the database

       search
           performs a search on users and returns those having a match between the search string
           passed via command line and one of the following fields:
           
           'user_id', 'user_name', 'e_mail'

       Adding a user to the database requires a user ID, a username and an e-mail address.
       A valid userID is required when deleting a user from the database.

       IMPORTANT NOTES:
       the VOSpace Transfer Service automatically populates the 'users' table in the database
       by previously quering the authentication system (RAP).
       It also adds the user node in the 'node' table, if not present (e.g. '/john.smith' for 
       the user 'john.smith').
       So, please, use this client only if you need to handle situations involing users that
       for some reason are not recognized by the authentication system.
    """)
    
# Create new VOSUser object
vosUserCli = VOSUser()

# Check the number of input args
if len(sys.argv) == 2:
    script, cmd = sys.argv
    if cmd == "add":
        vosUserCli.add()
    elif cmd == "del":
        vosUserCli.delete()
    else:
        vosUserCli.help()
elif len(sys.argv) == 3:
    script, cmd, arg = sys.argv
    if cmd == "search":
        vosUserCli.search(arg)
    else:
        vosUserCli.help()
else:
    vosUserCli.help()
