import difflib
import os.path
import sys
from pathlib import Path
from tempfile import NamedTemporaryFile

import click

from .engine import SedgeEngine, ConfigOutput
from .keylib import KeyLibrary
from .templates import sedge_config_header


def get_file_backup_name(file_name):
    return file_name + '.pre_sedge'


def ask_overwrite(file_name):
    click.echo("WARNING: '{}' already exists and was not generated by sedge.".format(file_name), err=True)
    confirm = input("Enter 'yes' to overwrite (I'll make a backup called '{backup_file}'): ".format(
        backup_file=get_file_backup_name(file_name))
    )
    return confirm == 'yes'


def backup_file(file_name):
    _backup_file = get_file_backup_name(file_name)
    os.rename(file_name, _backup_file)
    click.echo('Your previous SSH configuration file has been renamed to: {}'.format(_backup_file), err=True)


def check_or_confirm_overwrite(file_name):
    """
    Returns True if OK to proceed, False otherwise
    """
    try:
        with open(file_name) as fd:
            header = next(fd)
            if header.find(':sedge:') == -1:
                okay = ask_overwrite(file_name)
                if okay:
                    backup_file(file_name)
                else:
                    return False

    except FileNotFoundError:
        click.echo("{} not found".format(file_name), err=True)
    except StopIteration as e:
        click.echo(repr(e), err=True)
    else:
        return True


def diff_config_changes(before, after):
    def get_data(f):
        try:
            with open(f) as fd:
                return fd.read().splitlines(True)
        except FileNotFoundError:
            click.echo("{} not found".format(f), err=True)
            return []

    a = get_data(before)
    b = get_data(after)
    diff_lines = list(difflib.unified_diff(a, b))
    if not diff_lines:
        click.echo('no changes.', err=True)
    else:
        click.echo('configuration changes:', err=True)
        click.echo(''.join(diff_lines), err=True)


class SedgeConfig:
    pass


sedge_config = click.make_pass_decorator(SedgeConfig, ensure=True)


@click.group()
@click.version_option()
@click.option('-c',
              '--config-file',
              default=os.path.expanduser('~/.sedge/config'), )
@click.option('-o',
              '--output-file',
              default=os.path.expanduser('~/.ssh/config'), )
@click.option('-n',
              '--no-verify',
              is_flag=True,
              help='do not verify HTTPS requests')
@click.option('-k',
              '--key-directory',
              default=os.path.expanduser('~/.ssh'),
              help='directory to scan for SSH keys', )
@click.option('-v', '--verbose', count=True, default=0)
@sedge_config
def cli(config, verbose, key_directory, no_verify, output_file, config_file):
    """
    Template and share OpenSSH ssh_config(5) files. A preprocessor for
    OpenSSH configurations.
    """
    config.verbose = verbose
    config.key_directory = key_directory
    config.config_file = config_file
    config.output_file = output_file
    config.no_verify = no_verify


@cli.command('init')
@sedge_config
def init(config):
    """
    Initialise ~./sedge/config file if none exists.
    Good for first time sedge usage
    """
    from pkg_resources import resource_stream
    import shutil

    config_file = Path(config.config_file)
    if config_file.is_file():
        click.echo('{} already exists, maybe you want $ sedge update'.format(config_file))
        sys.exit()

    config_file.parent.mkdir(parents=True, exist_ok=True)
    with resource_stream(__name__, 'sedge_template.conf') as src_stream:
        with open(config.config_file, 'wb') as target_stream:
            shutil.copyfileobj(src_stream, target_stream)


@cli.command('update')
@sedge_config
def update(config):
    """
    Update ssh config from sedge specification
    """

    def write_to(out):
        engine.output(out)

    config_file = Path(config.config_file)
    if not config_file.is_file():
        click.echo('No file {} '.format(config_file), err=True)
        sys.exit()

    library = KeyLibrary(config.key_directory)
    with config_file.open() as fd:
        engine = SedgeEngine(library, fd, not config.no_verify, url=config.config_file)

    if config.output_file == '-':
        write_to(ConfigOutput(sys.stdout))
        return

    if not check_or_confirm_overwrite(config.output_file):
        click.echo('Aborting.', err=True)
        sys.exit(1)

    tmp_file = NamedTemporaryFile(mode='w', dir=os.path.dirname(config.output_file), delete=False)
    try:
        tmp_file.file.write(sedge_config_header.format(config.config_file))
        write_to(ConfigOutput(tmp_file.file))
        tmp_file.close()
        if config.verbose:
            diff_config_changes(config.output_file, tmp_file.name)
        os.rename(tmp_file.name, config.output_file)
    except:
        os.unlink(tmp_file.name)
        raise


@cli.group()
@sedge_config
def keys(config):
    """
    Manage ssh keys
    """


@keys.command('list')
@sedge_config
def command_list_keys(config):
    library = KeyLibrary(path=config.key_directory, verbose=config.verbose)
    library.list_keys()


@keys.command('add')
@sedge_config
def command_add_keys(config):
    library = KeyLibrary(path=config.key_directory, verbose=config.verbose)
    library.add_keys()
