from __future__ import print_function
import os, sys
import argparse
from typtop.dbaccess import (
    UserTypoDB,
    call_check
)
from typtop.config import (
    SEC_DB_PATH, NUMBER_OF_ENTRIES_TO_ALLOW_TYPO_LOGIN,
    WARM_UP_CACHE, VERSION, GROUP, DISTRO, BINDIR, first_msg,
)
from typtop.validate_parent import is_valid_parent
import subprocess

USER = ""
SEND_LOGS_SCRIPT = '{}/send_typo_log.py'.format(BINDIR)
if not os.path.exists(SEND_LOGS_SCRIPT):
    SEND_LOGS_SCRIPT = '/usr/bin/send_typo_log.py'
    if not os.path.exists(SEND_LOGS_SCRIPT):
        SEND_LOGS_SCRIPT = 'send_typo_log.py'

ALLOW_TYPO_LOGIN = True


class AbortSettings(RuntimeError):
    pass

def _get_login_user():
    # gets the username of the logging user
    pp = subprocess.Popen('who', stdout=subprocess.PIPE)
    output = pp.stdout.read()
    first_line = output.splitlines()[0]
    user = first_line.split()[0]
    return user


def _get_username():
    # trying to go over the problem of
    if USER:
        print("Designated user: {}".format(USER))
        return USER
    uid = os.getuid()
    is_root = uid == 0
    user = _get_login_user()
    if is_root:
        r = raw_input("Setting will be done for login user: {}.\n"
                      "Please confirm. (Yn) ".format(user))
        abort = r and r.lower() == "n"
        if abort:
            raise AbortSettings()
    else:
        print("Designated user: {}".format(user))
    return user


def _get_typoDB():
    user = _get_username()
    try:
        typoDB = UserTypoDB(user)
    except Exception as e:
        print(
            "It seems you have not initialized the db. Try running"\
            " \"sudo {} --init\" to initialize the db.\nThe error "\
            "I ran into is the following:\n{}"
            .format(sys.argv[0], e)
        )
        return None
    if not typoDB.is_typtop_init():
        raise Exception("{}:{} not initiated".format(
            str(typoDB),
            typoDB.get_db_path())
        )
    return typoDB


def root_only_operation():
    if os.getuid() != 0:
        print("ERROR!! You need root privilege to run this operation")
        raise AbortSettings


def initiate_typodb(RE_INIT=False):
    # ValueError(
    #     "You should not require to call this. "
    #     "Something is wrong!! Try re-installing the whole system"
    # )
    root_only_operation()
    # user = _get_username()
    # try:
    #     # checks that such a user exists:
    #     _ = pwd.getpwnam(user).pw_dir
    # except KeyError as e:
    #     print("Error: {}".format(e.message))
    #     print("Hint: The user ({}) must have an account in this computer."\
    #           .format(user))
    #     print("Hint 2: It's not a registration. User the username for "\
    #           "your account in the computer.")
    if False:
        pass
    else:
        branch = "master"
        subdir, download_bin, makecmd = '', '', ''
        if DISTRO == 'darwin':
            # TODO: Cleanup this directories. e.g., pam_opendirectory
            subdir = 'csrcs/osx/prebuilt'
            download_bin = "curl -LO"
            makecmd = './install.sh'
        elif DISTRO in ('debian', 'fedora', 'arch'):
            subdir = 'csrcs/linux/'
            download_bin = "wget"
            makecmd = "make && make install"
        cmd = """
        cd /tmp/ && {download_bin} https://github.com/rchatterjee/pam-typopw/archive/{branch}.zip && unzip -qq {branch}.zip \
        && cd pam-typopw-{branch}/{subdir} && {makecmd};
        cd /tmp && rm -rf {branch}.zip pam-typopw*
        """.format(branch=branch, subdir=subdir,
                   download_bin=download_bin, makecmd=makecmd)
        print(cmd)
        os.system(cmd)

common_auth = {   # Not used
    'debian': '/etc/pam.d/common-auth',
    'fedora': '/etc/pam.d/system-auth',
    'darwin': '',
    'arch'  : '/etc/pam.d/system-auth',
}[DISTRO]


def uninstall_pam_typtop():
    # Last try to send logs
    root_only_operation()
    typtop_uninstall_script = BINDIR + '/typtop-uninstall.sh'
    print(DISTRO)
    subprocess.call(typtop_uninstall_script)

parser = argparse.ArgumentParser("typtop ")
# parser.add_argument(
#     "--user",
#     help="To set the username. Otherwise login user will be the target"
# )

parser.add_argument(
    "--init", action="store_true",
    help="To initialize the DB. You have to run this once you install pam_typtop"
)

parser.add_argument(
    "--allowtypo", type=str.lower, choices=['yes','no'],
    help='Allow login with typos of the password'
)

parser.add_argument(
    "--allowupload", type=str.lower, choices=['yes', 'no'],
    help="Allow uploading the non-sensitive anonymous "\
    "data into the server for research purposes."
)

# parser.add_argument(
#     "--installid", action="store_true",
#     help="Prints the installation id, which you have to submit while filling up the google form"
# )

parser.add_argument(
    "--status", action="store", nargs="+",
    help='Prints current states of the typo-tolerance. Needs a username as argument.'
)

parser.add_argument(
    "--uninstall", action="store_true",
    help="Uninstall TypToP from your machine. Will delete all the data related to TypTop too."
)

# parser.add_argument(
#     "--reinit", action="store_true",
#     help="To re-initiate the DB, especially after the user's pw has changed"
# )

parser.add_argument(
    "--update", action="store_true",
    help="Updates TypTop to the latest released version"
)

parser.add_argument(
    "--check", action="store", nargs=3,
    help="(INTERNAL FUNCTION. PLEASE DON'T CALL THIS.)"
)

parser.add_argument(
    "--debug", action="store_true",
    help="Prepare report for debugging"
)

def main():
    args = parser.parse_args()
    if len(sys.argv) <=1:
        print(parser.print_help())
        sys.exit(0)

    # ITS IMPORTANT THIS ONE WILL BE FIRST
    # if args.user:
    #     global USER
    #     USER = args.user
        # print("User settings have been set to {}".format(USER))
    try:
        # root_only_operation()
        if args.allowtypo:
            typoDB = _get_typoDB()
            if args.allowtypo == "no":
                typoDB.allow_login(False)
                print(
                    """
Turning OFF login with typos. The software will still monitor your
typos and build cache of popular typos. You can switch on this
whenever you want.
                    """)  # :{}".format(typoDB.is_allowed_login())
            elif args.allowtypo == "yes":
                print("Turning ON login with typos...",)
                typoDB.allow_login(True)

        if args.allowupload:
            typoDB = _get_typoDB()
            if args.allowupload == "yes":
                typoDB.allow_upload(True)
                print("Uploading data is enabled. You are awesome. Thanks!!")
            elif args.allowupload == "no":
                typoDB.allow_upload(False)
                print("Uploading data is disabled.  :( :'( :-(!")
                print("Thanks for using the software anyway.")

        if args.init:
            print(first_msg, file=sys.stderr)
            print("Initializing the typo database..")
            initiate_typodb()

        # if args.reinit:
        #     print("RE-initiating pam_typtop")
        #     initiate_typodb(RE_INIT=True)

        if args.status:
            users = args.status
            if not users:
                users.add(_get_username())
            for user in users:
                typoDB = UserTypoDB(user)
                print("\n** TYPO-TOLERANCE STATUS **\n")
                print(">> User: {}".format(user))
                print("\tLogin with typos: {}".format(typoDB.is_allowed_login()))
                print("\tParticipate in the study: {}"\
                      .format(typoDB.is_allowed_upload()))
                print("\tIs enough logins to allow typos: {}"\
                      .format(typoDB.check_login_count(update=False)))
                print("\tInstall Id: {}".format(typoDB.get_installation_id().strip()))
                print("\tSoftware Version: {}".format(VERSION))
                print("\tNum entries before typo-login allowed: {}".format(NUMBER_OF_ENTRIES_TO_ALLOW_TYPO_LOGIN))
                print("\tWarmup cache: {}".format(WARM_UP_CACHE))

        if args.uninstall:
            r = raw_input("Uninstalling pam_typtop. Will delete all the "
                          "databases.\nPlease confirm. (yN)")
            if r and r.lower() == "y":
                uninstall_pam_typtop()

        if args.update:  # delete all old data
            cmd =  """export PIP_FORMAT=columns;
                pip list --outdated|grep typtop;
                if [ "$?" = "0" ]; then
                   pip uninstall typtop
                   pip install -U --ignore-installed typtop && typtops.py --init
                else
                   echo "Already uptodate! No need to update."
                fi
            """
            os.system(cmd)

        if args.debug:
            p = subprocess.Popen(
                'pip show numpy', shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT
            )
            numpypath = ''
            for l in p.stdout:
                l = l.strip().split(': ', 1)
                if l[0] == 'Location':
                    numpypath = l[1]
                    break
            print("Is numpy in path: {}".format(numpypath in sys.path))
            proc = subprocess.Popen(
                # TODO: Add numpy path constraint

                """
                set -x
                set -u

                users
                # su $USER -c "which su"
                # <enter correct password>
                # if [ $? -neq 0 ]; then exit; else "echo password incorrect"; fi
                typtop --status $USER
                sudo ls -altrh  /usr/local/etc/typtop.d/$USER/typtop.json
                ls -altrh $(which su) $(which typtop)
                python -c "import pwd; print pwd.getpwnam('$USER')"
                tail -n50 /var/log/typtop.log
                """, shell=True,
                stdout=subprocess.PIPE,
                # stderr=subprocess.STDOUT
                stderr=sys.stdout.fileno()
            )
            print(proc.stdout.read())

        if args.check:
            # ensure the parent is pam_opendirectory_typo.so
            assert is_valid_parent()
            failed, user, pw = args.check
            ret = call_check(failed, user, pw)
            sys.stdout.write(str(ret))
            # if ret==0:
            #     p = subprocess.Popen([SEND_LOGS_SCRIPT, user])

    except AbortSettings:
        print("Settings' change had been aborted.")


if __name__ == '__main__':
    main()
