#!/usr/bin/env python3
"""\
Enable python -m prophane

adapted from https://github.com/dib-lab/charcoal/blob/dfc18387a7f88abb77941a5c0528b924bc43b237/charcoal/__main__.py
"""

import click
import os
import subprocess
import sys

from utils.config import get_database_base_dir, get_general_cfg_path
from utils.db_handling.db_access import print_summary_of_installed_dbs, is_database_schema_version_outdated
from utils.db_handling.maintenance.migrate_database_schema_version import migrate_dbs_style_if_necessary


def get_snakefile_path(name):
    thisdir = os.path.dirname(os.path.realpath(__file__))
    snakefile = os.path.join(thisdir, name)
    return snakefile


def get_package_configfile(filename):
    thisdir = os.path.dirname(os.path.realpath(__file__))
    configfile = os.path.join(thisdir, 'conf', filename)
    return configfile


def run_prophane_workflow(configfile, no_use_conda=False, verbose=False,
                          snakefile_name='Snakefile', extra_args=None):
    if extra_args is None:
        extra_args = []

    try:
        db_base_dir = get_database_base_dir(configfile)
    except LookupError as e:
        print('Error: Database directory not found, did you execute "prophane init"?', file=sys.stderr)
        sys.exit(1)

    if not os.path.exists(db_base_dir):
        # dir does not exist, continue
        pass
    # if db_base_dir is not empty, check versions of installed DBs
    elif os.listdir(db_base_dir):
        if is_database_schema_version_outdated(db_base_dir):
            print(f"Migrating database folder '{db_base_dir}'...", file=sys.stderr)
            migrate_dbs_style_if_necessary(db_base_dir)
            print(f"Done", file=sys.stderr)

    # find the Snakefile relative to package path
    snakefile = get_snakefile_path(snakefile_name)

    # basic command
    cmd = ["snakemake", "-s", snakefile]

    # add --use-conda
    if not no_use_conda:
        cmd += ["--use-conda"]

    # snakemake sometimes seems to want a default number of cores; set it to the number of available cores for now.
    # can overridden later on command line.
    cmd += ["--cores"]

    # add rest of snakemake arguments
    cmd += list(extra_args)

    if configfile:
        # add defaults and system config files, in that order
        configfiles = [get_package_configfile("defaults.conf"),
                       get_package_configfile("system.conf"),
                       configfile]
        cmd += ["--configfile", configfile]

    if verbose:
        print('final command:', *cmd)

    # runme
    try:
        subprocess.check_call(cmd)
    except subprocess.CalledProcessError as e:
        print(f'Error in snakemake invocation: {e}', file=sys.stderr)
        return e.returncode


#
# actual command line functions
#

@click.group()
def cli():
    pass


def single_yes_or_no_question(question, default_no=True):
    choices = ' [y/N]: ' if default_no else ' [Y/n]: '
    default_answer = 'n' if default_no else 'y'
    reply = str(input(question + choices)).lower().strip() or default_answer
    if reply[0] == 'y':
        return True
    if reply[0] == 'n':
        return False
    else:
        return False if default_no else True


@click.command()
@click.argument('db-dir', type=click.Path(writable=True, file_okay=False, resolve_path=True))
def init(db_dir):
    """
    Write DB_DIR path for storing prophane databases to general config file.
    """
    from utils.vars import DEFAULT_GENERAL_CFG
    cfg_f = DEFAULT_GENERAL_CFG

    # check for existing config file
    try:
        cfg_f = get_general_cfg_path()
    except FileNotFoundError:
        pass
    else:
        print(f"General config found: {cfg_f}")
        if not single_yes_or_no_question("Overwrite?"):
            sys.exit(0)

    # write db-dir path to general config
    with open(cfg_f, 'wt') as f_out:
        print(f"Writing general config {cfg_f}")
        f_out.write(f"""\
db_base_dir: {db_dir}
""")
    sys.exit(0)


# create a run subcommand that by default passes all of its arguments
# on to snakemake (after setting Snakefile and config)
@click.command(context_settings={"ignore_unknown_options": True})
@click.argument('configfile')
@click.option('--verbose', is_flag=True)
@click.argument('snakemake_args', nargs=-1)
def run(configfile, snakemake_args, verbose):
    "execute prophane workflow (using snakemake underneath)"
    sys.exit(run_prophane_workflow(configfile, verbose=verbose,
                          extra_args=snakemake_args))


# download and preprocess databases
@click.command(context_settings={"ignore_unknown_options": True})
@click.argument('configfile')
@click.option('--verbose', is_flag=True)
@click.argument('snakemake_args', nargs=-1)
def prepare_dbs(configfile, snakemake_args, verbose):
    """download the databases required to execute the tasks in the provided CONFIGFILE"""
    sys.exit(run_prophane_workflow(configfile, verbose=verbose,
                          extra_args=snakemake_args + ["db_setup"]))


@click.command()
def list_dbs():
    """
    list installed databases
    """
    try:
        db_base_dir = get_database_base_dir()
    except LookupError as e:
        print('Error: Database directory not found, did you execute "prophane init"?', file=sys.stderr)
        sys.exit(1)

    print_summary_of_installed_dbs(db_base_dir)
    sys.exit(0)


@click.command()
def list_styles():
    """
    list available input file styles
    """
    from utils.search_result_parsing.style_handling import print_available_input_file_styles
    print_available_input_file_styles()
    sys.exit(0)


# 'info' command
@click.command()
def info():
    """provide basic install/config file info"""
    from utils.vars import PROPHANE_VERSION as VERSION
    try:
        general_cfg = get_general_cfg_path()
    except FileNotFoundError:
        general_cfg = "None"
    print(f"""
This is prophane version v{VERSION}

Package install path: {os.path.dirname(__file__)}
General config file: {general_cfg}
snakemake Snakefile: {get_snakefile_path('Snakefile')}
""")


cli.add_command(init)
cli.add_command(run)
cli.add_command(info)
cli.add_command(prepare_dbs)
cli.add_command(list_dbs)
cli.add_command(list_styles)


def main():
    cli()


if __name__ == '__main__':
    main()
