"""
Application to configure variables used by obsinfo in file .obsinforc
and to copy examples and templates out of the distribution directories
"""
import os
# import sys
import re
# import site
import shutil
import datetime
import platform
from pathlib import Path
import warnings

from argparse import ArgumentParser

from ..misc.const import EXIT_IOERR
from ..version import __version__
from ..helpers import init_logging

logger = init_logging("setup")


def main():
    """
    Entry point to configure variables in file .obsinforc

    and to copy examples and templates out of the distribution directories
    """
    # initialize variables
    version = platform.python_version()
    path_to_package = Path(__file__).parent.parent
    examples_dir = path_to_package.joinpath("_examples/Information_Files")
    templates_dir = path_to_package.joinpath("templates")
    home = Path.home()
    configuration_file = home.joinpath('.obsinforc')

    args = retrieve_arguments()

    validate_arguments(args)

    # create logging directory if it doesn't exist
    logdir = Path().home().joinpath('.obsinfo').resolve()
    if not os.path.isdir(logdir):
        os.mkdir(logdir)

    # copy examples and templates
    if args.dest and not args.no_copy:
        try:
            dest = Path(args.dest).resolve()
            # if dest dir exists rename it with timestamp to preserve it
            if os.path.isdir(dest) or os.path.isfile(dest):
                new_dest = str(dest) + "." + str(datetime.datetime.today())
                os.rename(dest, new_dest)

            shutil.copytree(examples_dir, dest.joinpath("Information_Files"))
            shutil.copytree(templates_dir, dest.joinpath("templates"))
        except NotADirectoryError:
            print("Directory not found")
            exit(EXIT_IOERR)
        except OSError as e:
            print("Operating system error")
            print(e)
            exit(EXIT_IOERR)
    # create configuration file
    output = []

    output.append("gitlab_repository: " + args.gitlab + "\n")
    output.append("gitlab_project: " + args.project + "\n")
    output.append("gitlab_path: " + args.remote_path + "\n")
    pat = re.compile("^[0-9]+\.[0-9]+")
    version = pat.match(__version__)
    branch = "v" + version.group(0) if args.branch else "master"

    output.append('obsinfo_branch: ' + branch + '\n')

    remote_repository = args.gitlab + "/" + args.project + "/" + args.remote_path \
        if not args.no_remote else ""

    s = build_datapath_list(args, remote_repository)
    if s:
        output.append(s + "\n")
    try:
        with open(configuration_file, "w") as fp:
            fp.writelines(output)
    except OSError:
        warnings.warn("Could not create configuration file. "
                      "Fix problem and run obsinfo-setup again\n")
        exit(EXIT_IOERR)


def retrieve_arguments():
    """
    Parse command line arguments

    Returns:
        parse_args (dict): arguments, both status and directory names

    """
    # Parse the arguments
    parser_args = ArgumentParser(prog="obsinfo-setup")

    # flags
    parser_args.add_argument("-x", "--no_examples", action='store_true', default=False,
                             help="Don't import examples, only templates, and "
                                  "remove examples directory from the datapath")
    parser_args.add_argument("-c", "--no_copy", action='store_true', default=False,
                             help="Don't import anything at all, don't create "
                                  "dest directory, which will be removed from datapath")
    parser_args.add_argument("-n", "--no_remote", action='store_true', default=False,
                             help="Install obsinfo without access to a gitlab "
                             "repository.\nMay be needed in some operating "
                             "systems for compatibility")
    parser_args.add_argument("-v", "--invert_datapath", action='store_true', default=False,
                             help="Put remote gitlab repositorey first. "
                                  "All local directories will keep their order")
    parser_args.add_argument("-b", "--branch", action='store_true', default=False,
                             help="Specifies the git branch to use, if not master")

    # optional arguments
    parser_args.add_argument("-d", "--dest", default=None,
                             help="Destination directory for templates and examples.")
    parser_args.add_argument("-g", "--gitlab", default="https://www.gitlab.com",
                             help="Gitlab repository)")
    parser_args.add_argument("-p", "--project", default="resif/obsinfo",
                             help="path to project and the directory where "
                                  "information files lie within the Gitlab "
                                  "repository")
    parser_args.add_argument("-l", "--local_repository", default=None,
                             help="Specify local repository for information "
                                  "files and include it as first or second "
                                  "option in datapath")
    parser_args.add_argument("-w", "--working_directory", default=None,
                             help="Specify working directory for obsinfo and "
                                  "include it as first option in datapath")
    parser_args.add_argument("-P", "--remote_path",
                             default="obsinfo/_examples/Information_Files",
                             help="Specify remote directory under project")
    return parser_args.parse_args()


def validate_arguments(args):
    """
    Validate that directories in command line arguments exist.

    Raise an exception if not.
    Args:
        args (dict of str): directory with command line argument, but status
            and directories
    Raises:
        OSError, ValueError
    """

    if args.dest:
        if not Path(args.dest):
            raise OSError(ENOENT, "Path for example and template destination "
                                  "directory is an illegal path")
    else:
        default_information_files = ""

    if args.local_repository:
        if not Path(args.local_repository):
            raise OSError(ENOENT, "Path for local repository for information "
                                  "files is an illegal path")

    if args.working_directory:
        if not Path(args.working_directory):
            raise OSError(ENOENT, "Path for obsinfo working directory is an "
                                  "illegal path")

    if not args.no_remote:
        if not Path(args.gitlab).as_uri:
            raise ValueError('Gitlab repository must be specified as a legal '
                             'URI unless "-n" is on')

    if not args.no_remote:
        if not Path(args.project):
            raise ValueError('Project must be specified as a legal URI unless '
                             '"-n" is on')


def build_datapath_list(args, remote_repository):
    """
    Build datapath as a list of directories to search
    
    Similar to the PATH environment variable.

    #. Add working_directory as first option if it exists
    #. Next add local repository first if it exists (wll be first if no
       working directory specified)
    #. Finally add destination, where standard examples lie.
       This won't be used if -x is on
    #. Final directory will be the remote one unless -v (invert) is specified
    """

    default_information_files = str(Path(args.dest).joinpath("Information_Files").resolve()) if args.dest else ""
    remote_first = remote_repository + ", " if args.invert_datapath else ""
    remote_last = remote_repository if not args.invert_datapath else ""

    s = "datapath: ["
    s += remote_first
    s += args.working_directory + ", " if args.working_directory else ""
    s += args.local_repository + ", " if args.local_repository else ""
    s += default_information_files + ", " if default_information_files and not args.no_examples else ""
    s += remote_last if remote_last else ""
    s += "]"

    if not remote_last:
        s.replace(", ]", "]")

    return s
