#!/usr/bin/env python3

import click
import json
from pathlib import Path
from pprint import pprint
import requests
import sys
import yaml


@click.group()
@click.option('--config', '-c', required=False,
              default=str(Path.home()) + "/hyperkube-config.yaml")
@click.pass_context
def cli(ctx, config):
    ctx.ensure_object(dict)
    ctx.obj['hyperkube_config'] = _load_config(config)
    ctx.obj['url'] = ctx.obj['hyperkube_config']['url']
    ctx.obj['stage'] = ctx.obj['hyperkube_config']['stage']
    headers = {'X-Api-Key': ctx.obj['hyperkube_config']['x_api_key']}
    ctx.obj['headers'] = headers


@cli.command()
@click.pass_context
def list(ctx):
    """List all clusters in hyper-kube-config"""

    try:
        r = requests.get(
            f'{ctx.obj["url"]}/{ctx.obj["stage"]}/clusters/list',
            headers=ctx.obj['headers']
        )
        pprint(sorted(r.json()))
        if r.status_code != 200:
            sys.exit(1)
    except requests.exceptions.RequestException as err:
        print(f'Request error: {err}')


@cli.command()
@click.option('--k8s-config', '-k', default="~/.kube/config")
@click.pass_context
def add(ctx, k8s_config):
    """Add cluster to hyper-kube-config"""

    k8s_cfg = json.dumps(_load_config(k8s_config))
    print(k8s_cfg)

    try:
        r = requests.post(
            f'{ctx.obj["url"]}/{ctx.obj["stage"]}/clusters/add',
            headers=ctx.obj['headers'],
            data=k8s_cfg
        )
        pprint(r.json())
        if r.status_code == 404:
            sys.exit(1)
    except requests.exceptions.RequestException as err:
        print(f'Request error: {err}')


@cli.command()
@click.option('--ca-key', '-a', required=True)
@click.option('--cluster-name', '-n', required=True)
@click.pass_context
def add_ca_key(ctx, cluster_name, ca_key):
    """Add cluster CA key to hyper-kube-config"""

    key = _load_pem(ca_key)
    post = {"cluster_name": cluster_name, "ca_key": key}

    try:
        r = requests.post(
            f'{ctx.obj["url"]}/{ctx.obj["stage"]}/clusters/add-ca-key',
            headers=ctx.obj['headers'],
            data=post
        )
        pprint(r.json())
    except requests.exceptions.RequestException as err:
        print(f'Request error: {err}')


@cli.command()
@click.option('--cluster-name', '-n', required=True)
@click.pass_context
def remove_ca_key(ctx, cluster_name):
    """Remove specified cluster CA key from hyper-kube-config"""

    try:
        r = requests.get(
            (f'{ctx.obj["url"]}/{ctx.obj["stage"]}'
             f'/clusters/remove-ca-key?{cluster_name}'),
            headers=ctx.obj['headers']
        )
        pprint(r.json())
        if r.status_code == 404:
            sys.exit(1)
    except requests.exceptions.RequestException as err:
        print(f'Request error: {err}')


@cli.command()
@click.option('--cluster-to-remove', '-k', required=True)
@click.pass_context
def remove(ctx, cluster_to_remove):
    """Remove cluster from hyper-kube-config"""

    try:
        r = requests.post(
            f'{ctx.obj["url"]}/{ctx.obj["stage"]}/clusters/remove',
            headers=ctx.obj['headers'],
            data=json.dumps({"cluster_name": cluster_to_remove})
        )
        pprint(r.json())
        if r.status_code == 404:
            sys.exit(1)
    except requests.exceptions.RequestException as err:
        print(f'Request error: {err}')


@cli.command()
@click.option('--cluster', '-g', multiple=True)
@click.pass_context
def get(ctx, cluster):
    """Retrieve and concatenate one or more cluster configs into one config,
    set context to first cluster"""

    param_string = ""
    for c in cluster:
        param_string = param_string + c + "&"

    # remove trailing '&'
    param_string = param_string[:-1]

    try:
        r = requests.get(
            (f'{ctx.obj["url"]}/{ctx.obj["stage"]}/clusters/get-k8-config?'
             f'{param_string}'),
            headers=ctx.obj['headers']
        )
        pprint(r.json())
        if r.status_code == 404:
            sys.exit(1)
    except requests.exceptions.RequestException as err:
        print(f'Request error: {err}')


@cli.command()
@click.pass_context
def get_all(ctx):
    """Retrieve and concatenate all cluster configs into one config"""

    try:
        r = requests.get(
            f'{ctx.obj["url"]}/{ctx.obj["stage"]}/clusters/get-all-k8-configs',
            headers=ctx.obj['headers']
        )
        pprint(r.json())
        if r.status_code == 404:
            sys.exit(1)
    except requests.exceptions.RequestException as err:
        print(f'Request error: {err}')


@cli.command()
@click.option('--cluster', '-g')
@click.pass_context
def get_pem(ctx, cluster):
    """Get the pem file for a specific cluster"""

    try:
        r = requests.get(
            (f'{ctx.obj["url"]}/{ctx.obj["stage"]}'
             f'/clusters/get-pem?cluster_name={cluster}'),
            headers=ctx.obj['headers']
        )
        pprint(r.text)
        if r.status_code == 404:
            sys.exit(1)
    except requests.exceptions.RequestException as err:
        print(f'Request error: {err}')


@cli.command()
@click.option('--cluster', '-g', required=True)
@click.option('--pem', '-p', required=True)
@click.pass_context
def add_pem(ctx, cluster, pem):
    """Add a pem file for a specific cluster"""

    pem_data = _load_pem(pem)

    try:
        r = requests.post(
            (f'{ctx.obj["url"]}/{ctx.obj["stage"]}'
             f'/clusters/add-pem?cluster_name={cluster}'),
            headers=ctx.obj['headers'],
            data=pem_data
        )
        pprint(r.json())
        if r.status_code == 404:
            sys.exit(1)
    except requests.exceptions.RequestException as err:
        print(f'Request error: {err}')


@cli.command()
@click.option('--environment', '-g')
@click.option('--status', '-s')
@click.option('--cluster', '-g', required=True)
@click.pass_context
def set_cluster_status(ctx, environment, status, cluster):
    """Set cluster status, you are able to update both environment or status"""

    if environment:
        try:
            print(f'Setting cluster environment to {environment}')
            r = requests.get(
                (f'{ctx.obj["url"]}/{ctx.obj["stage"]}'
                 f'/clusters/set-cluster-environment?cluster_name={cluster}'
                 f'&environment={environment}'),
                headers=ctx.obj['headers']
            )
            if r.status_code == 404:
                sys.exit(1)
        except requests.exceptions.RequestException as err:
            print(f'Request error: {err}')
    if status:
        try:
            print(f'Setting cluster status {status}')
            r = requests.get(
                (f'{ctx.obj["url"]}/{ctx.obj["stage"]}'
                 f'/clusters/set-cluster-status?cluster_name={cluster}'
                 f'&cluster_status={status}'),
                headers=ctx.obj['headers']
            )
            if r.status_code == 404:
                sys.exit(1)
        except requests.exceptions.RequestException as err:
            print(f'Request error: {err}')

    if not status and not environment:
        print(('Please provide either --environment or --status flag'
               'for updating cluster'))


@cli.command()
@click.option('--environment', '-g', required=True)
@click.option('--status', '-s', required=True)
@click.pass_context
def get_cluster_status(ctx, environment, status):
    """Get clusters of a given status in a particular environment"""

    try:
        r = requests.get(
            (f'{ctx.obj["url"]}/{ctx.obj["stage"]}'
             f'/clusters/cluster-status?cluster_status={status}'
             f'&environment={environment}'),
            headers=ctx.obj['headers']
        )
        pprint(r.json())
        if r.status_code == 404:
            sys.exit(1)
    except requests.exceptions.RequestException as err:
        print(f'Request error: {err}')


def _load_config(config):
    """Loads yaml config to dict object"""
    try:
        with open(config, 'r') as ymlfile:
            cfg = yaml.load(ymlfile, Loader=yaml.FullLoader)
            return cfg
    except FileNotFoundError as err:
        print(f'Error: {err}')
        sys.exit(1)


def _load_pem(pem):
    """Open and return a pem file as object"""
    try:
        with open(pem, 'r') as pemfile:
            return pemfile.read()
    except FileNotFoundError:
        print(f'Provided pem not found: {pem}')
        sys.exit(1)


if __name__ == '__main__':
    cli(obj={})
