#! /usr/bin/env python3
"""Script to replace environment keys in given directory"""

import os
import sys
import argparse
from bdb import BdbQuit


from pysyte import args as arguments
from pysyte.paths import path
from pysyte.paths import home


__version__ = '0.1.0'


class ScriptError(NotImplementedError):
    pass


def run_args(args, methods):
    """Run any methods eponymous with args"""
    if not args:
        return False
    valuable_args = {k for k, v in args.__dict__.items() if v}
    arg_methods = {methods[a] for a in valuable_args if a in methods}
    for method in arg_methods:
        method(args)


def version(args):
    print('%s %s' % (args, __version__))
    raise SystemExit


def parse_args(methods):
    """Parse out command line arguments"""
    parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    parser.add_argument('directory',
                        help='path to be abbreviated')
    parser.add_argument('-o', '--only_home', action='store_true',
                        help='only replace $HOME with ~')
    parser.add_argument('-x', '--exclude', type=str, nargs='*',
                        help='exclude environment symbol')
    parser.add_argument('-v', '--version', action='store_true',
                        help='Show version')
    args = parser.parse_args()
    run_args(args, methods)
    return args


def directory_parts(directory):
    parts = path(directory).splitall()
    try:
        parts.remove('/')
    except ValueError:
        pass
    return parts


def matches(directory, exclude):
    result = {}
    for key, value in os.environ.items():
        if key in exclude:
            continue
        if not value:
            continue
        if '/' in value:
            if ':' in value:
                continue
            if value in directory:
                result[key] = value
    return result.items()


def trail_dir(string):
    if os.path.isdir(string):
        return string.rstrip('/') + '/'
    return ''


def replace_links(directory, links):
    result = []
    directory_ = trail_dir(directory)
    for source, destination in links:
        source_ = trail_dir(source)
        destination_ = trail_dir(destination)
        if not (source_ and destination_):
            continue
        if directory_.startswith(source_):
            result.append(directory_.replace(source_, destination_))
        elif directory_.startswith(destination_):
            result.append(directory_.replace(destination_, source_))
    return [_.rstrip('/') for _ in result]


def split_all(p):
    first, rest = os.path.split(p)
    if not first or first in '/~':
        return [first, rest]
    return split_all(first) + [rest]


def shortest(items):
    lengths = [(len(split_all(replace_home(_))), len(_), _) for _ in items]
    return sorted(lengths)[0][2]


def replace_home(directory):
    homes = [os.environ['HOME'], '$HOME']
    for home_ in homes:
        if directory.startswith(home_):
            return directory.replace(home_, '~', 1)
    return directory


def real_path(p):
    return os.path.realpath(os.path.expanduser(p))


def abs_path(p):
    return os.path.abspath(os.path.expanduser(p))


def home_links():
    links = [(
        str(x),
        str(x.realpath())
    ) for x in home().listdir() if x.islink() and x.isdir()]
    return sorted(links)


def sub_script(directory, args):
    if args.only_home:
        return directory
    replacements = []
    links = home_links()
    replacements += replace_links(directory, links or [])
    try:
        replacer = shortest(replacements)
        result = replace_home(replacer)
        return result
    except IndexError:
        return None


def script(args):
    directory = real_path(args.directory)
    new_directory = sub_script(directory, args)
    if not new_directory:
        print(replace_home(directory))
        return False
    print(replace_home(new_directory))
    return True


def main():
    """Run the script"""
    try:
        args = parse_args(globals())
        return os.EX_OK if script(args) else not os.EX_OK
    except BdbQuit:
        pass
    except SystemExit as e:
        return e.code
    return os.EX_OK


if __name__ == '__main__':
    sys.exit(arguments.call_main(main))
