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

import os
import sys
import argparse
from bdb import BdbQuit


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(main())
