#!/usr/bin/env python

from contextlib import contextmanager
import os, sys, argparse
import hcl
import click
import pprint
sys.path.insert(1, os.path.join(sys.path[0], '..'))
from tf_api_gateway import apiGateway

pp = pprint.PrettyPrinter(indent=2)

@contextmanager
def log_call(msg):
    click.echo("Starting {}".format(msg))
    yield
    click.echo("Finished {}".format(msg))

def load_terraform(template_file):
    with open(template_file, 'r') as fp:
        obj = hcl.load(fp)

    for k, v in enumerate(obj['variable']):
        if ( 'default' not in obj['variable'][v] ):
            obj['variable'][v]['default'] = ''
            

    return(obj)

def dict_compare(dict1, dict2):
    d1_keys = set(dict1.keys())
    d2_keys = set(dict2.keys())
    intersect_keys = d1_keys.intersection(d2_keys)
    added_keys = d1_keys - d2_keys
    removed_keys = d2_keys - d1_keys
    modified_keys = {o : (dict1[o], dict2[o]) for o in intersect_keys if str(dict1[o]['default']) != str(dict2[o]['attributes']['value'])}
    same_keys = set(o for o in intersect_keys if str(dict1[o]['default']) == str(dict2[o]['attributes']['value']))
    return added_keys, removed_keys, modified_keys, same_keys



@click.group()
def cli():
    """Console utility for interfacing with Terraform Enterprise

    \b
    To see details for each command, use:
     terraformGateway [command] --help"""
    pass


@cli.command()
@click.option('--tf_template', help='Terraform template containing the variables')
@click.option('--workspace', help='Name of the terraform Workspace for this project')
@click.option('--tf_key', default=os.environ['ATLAS_TOKEN'], help='Access Token for Terraform (Uses ATLAS_TOKEN environment var if not provided)')
@click.option('--organization', default=os.environ['ATLAS_USERNAME'], help='Terraform Organization name (Uses ATLAS_USERNAME environment var if not provided)')
def compare(tf_template, workspace, tf_key, organization):
    """Compares variables between Terraform and TF File and outputs the findings"""
    templateData = load_terraform(tf_template)

    myGateway = apiGateway(api_token=tf_key, organization=organization, workspace=workspace)

    var_list = myGateway.getVariableList().get("data", [])
    currentVars = {d["attributes"]["key"]: d for d in var_list}

    added_keys, removed_keys, modified_keys, same_keys = dict_compare(templateData['variable'], currentVars)

    print('Variables Added:')
    print('\n')
    print("{:<8} {:<15}".format('Key','Variable'))
    print("{:<8} {:<15}".format('---','--------'))
    for k, v in enumerate(added_keys):
        label = v
        print("{:<8} {:<15}".format(k, label))

    print('\n')
    pp.pprint("Variables Removed:")
    print('\n')
    print("{:<8} {:<15}".format('Key','Variable'))
    print("{:<8} {:<15}".format('---','--------'))
    for k, v in enumerate(removed_keys):
        label = v
        print("{:<8} {:<15}".format(k, label))

    print('\n')
    pp.pprint("Variables Modified:")
    print('\n')
    print("{:<8} {:<15}".format('Variable','New Value'))
    print("{:<8} {:<15}".format('--------','---------'))
    for k, v in modified_keys.items():
        label = v[0]['default']
        print("{:<8} {:<15}".format(k, label))

    print('\n')
    pp.pprint("Variables Unchanged:")
    print('\n')
    print("{:<8} {:<15}".format('Key','Variable'))
    print("{:<8} {:<15}".format('---','--------'))
    for k, v in enumerate(same_keys):
        label = v
        print("{:<8} {:<15}".format(k, label))

    print('\n')
    print('\n')

@cli.command()
@click.option('--tf_template', help='Terraform template containing the variables')
@click.option('--workspace', help='Name of the terraform Workspace for this project')
@click.option('--tf_key', default=os.environ['ATLAS_TOKEN'], help='Access Token for Terraform (Uses ATLAS_TOKEN environment var if not provided)')
@click.option('--organization', default=os.environ['ATLAS_USERNAME'], help='Terraform Organization name (Uses ATLAS_USERNAME environment var if not provided)')
def addNew(tf_template, workspace, tf_key, organization):
    """Adds all new variables from the TF file to Terraform"""

    templateData = load_terraform(tf_template)

    myGateway = apiGateway(api_token=tf_key, organization=organization, workspace=workspace)

    var_list = myGateway.getVariableList().get("data", [])
    currentVars = {d["attributes"]["key"]: d for d in var_list}

    added_keys, removed_keys, modified_keys, same_keys = dict_compare(templateData['variable'], currentVars)
    
    with log_call("adding variables..."):
        tf_var_list = templateData['variable']
        with click.progressbar(added_keys) as keys_to_add:
            for k, v in enumerate(keys_to_add):
                var_name = v

                if ( not isinstance( tf_var_list[var_name]['default'], int ) and not isinstance( tf_var_list[var_name]['default'], dict )):
                    if (len(tf_var_list[var_name]['default']) > 0):
                        var_value = tf_var_list[var_name]['default']
                    else:
                        var_value = 'REPLACE_IN_ATLAS'
                else:
                    var_value = tf_var_list[var_name]['default']

                myGateway.addVariable(var_name, var_value)



@cli.command()
@click.option('--tf_template', help='Terraform template containing the variables')
@click.option('--workspace', help='Name of the terraform Workspace for this project')
@click.option('--tf_key', default=os.environ['ATLAS_TOKEN'], help='Access Token for Terraform (Uses ATLAS_TOKEN environment var if not provided)')
@click.option('--organization', default=os.environ['ATLAS_USERNAME'], help='Terraform Organization name (Uses ATLAS_USERNAME environment var if not provided)')
def removeMissing(tf_template, workspace, tf_key, organization):
    """Removes variables from Terraform that are no longer in TF file"""

    templateData = load_terraform(tf_template)

    myGateway = apiGateway(api_token=tf_key, organization=organization, workspace=workspace)

    var_list = myGateway.getVariableList().get("data", [])
    currentVars = {d["attributes"]["key"]: d for d in var_list}

    added_keys, removed_keys, modified_keys, same_keys = dict_compare(templateData['variable'], currentVars)

    if (len(removed_keys) < 1):
        print("No variables to remove")
        return(0)

    with log_call("deleting variables..."):
        with click.progressbar(removed_keys) as keys_to_remove:
            for k, v in enumerate(keys_to_remove):
                var_name = v

                myGateway.deleteVariable( var_name )


@cli.command()
@click.option('--tf_template', help='Terraform template containing the variables')
@click.option('--workspace', help='Name of the terraform Workspace for this project')
@click.option('--tf_key', default=os.environ['ATLAS_TOKEN'], help='Access Token for Terraform (Uses ATLAS_TOKEN environment var if not provided)')
@click.option('--organization', default=os.environ['ATLAS_USERNAME'], help='Terraform Organization name (Uses ATLAS_USERNAME environment var if not provided)')
def updateValues(tf_template, workspace, tf_key, organization):
    """Updates variables in Terraform with the values in the TF file"""
    templateData = load_terraform(tf_template)

    myGateway = apiGateway(api_token=tf_key, organization=organization, workspace=workspace)

    var_list = myGateway.getVariableList().get("data", [])
    currentVars = {d["attributes"]["key"]: d for d in var_list}

    added_keys, removed_keys, modified_keys, same_keys = dict_compare(templateData['variable'], currentVars)

    if (len(modified_keys) < 1):
        print("Nothing to update")
        return(0)

    with log_call("updating variables..."):
        tf_var_list = templateData['variable']
        with click.progressbar(modified_keys) as keys_to_update:
            for k, v in enumerate(keys_to_update):
                var_name = v
                var_value = tf_var_list[var_name]['default']

                if ( (not isinstance(var_value, dict)) and (len(str(var_value)) > 0 )):
                    myGateway.updateVariable( var_name, var_value )

                    
@cli.command()
@click.option('--tf_template', default='', help='Terraform template containing the workspace to check')
@click.option('--workspace', default='', help='Name of the terraform Workspace to check')
@click.option('--tf_key', default=os.environ['ATLAS_TOKEN'], help='Access Token for Terraform (Uses ATLAS_TOKEN environment var if not provided)')
@click.option('--organization', default=os.environ['ATLAS_USERNAME'], help='Terraform Organization name (Uses ATLAS_USERNAME environment var if not provided)')
def checkWorkspace(tf_template, workspace, tf_key, organization):
    """Checks to see if Workspace exists
    
    If not provided, workspace in TF file will be used. (Uses 'atlas' block)
    If TF file is not provided, then workspace is required.
    If both are provided, workspace will be used
    
    \b
    Returns:
        1 if exists, else 0

    """
    if ( len(tf_template) > 0):
        templateData = load_terraform(tf_template)

    if ( len(workspace) < 1 ):
        if ( 'atlas' in templateData ):
            temp_name = templateData['atlas']['name'].split('/')
            workspace = temp_name[1]
        else:
            print("Atlas config not found in TF. Exiting")
            return(2)

    myGateway = apiGateway(api_token=tf_key, organization=organization, workspace=workspace)

    current_workspaces = myGateway.getWorkspaceList()

    workspace_list = current_workspaces['data']
    for k, v in enumerate(workspace_list):
        if ( v['attributes']['name'] == workspace ):
            print("Workspace exists")
            return(1)

    print("Workspace not found")
    return(0)


@cli.command()
@click.option('--tf_template', help='Terraform template containing the workspace to add')
@click.option('--workspace', help='Name of the terraform Workspace to add')
@click.option('--tf_key', default=os.environ['ATLAS_TOKEN'], help='Access Token for Terraform (Uses ATLAS_TOKEN environment var if not provided)')
@click.option('--organization', default=os.environ['ATLAS_USERNAME'], help='Terraform Organization name (Uses ATLAS_USERNAME environment var if not provided)')
@click.option('--git_repo', default='', help='Name of the git repo for this project (Optional)')
def addWorkspace(tf_template, workspace, tf_key, organization, git_repo):
    """Adds a workspace to Terraform

    If not provided, workspace in TF file will be used.
    If TF file is not provided, then workspace is required.
    
    \b
    Returns:
        0 if successful, else 1    

    """
    if ( len(tf_template) > 0):
        templateData = load_terraform(tf_template)

    if ( len(workspace) < 1 ):
        if ( 'atlas' in templateData ):
            temp_name = templateData['atlas']['name'].split('/')
            workspace = temp_name[1]
        else:
            print("Atlas config not found in TF. Exiting")
            return(2)

    myGateway = apiGateway(api_token=tf_key, organization=organization)

    if ( len(git_repo) > 0):
        print("Not yet implemented")
    else:
        response = myGateway.addWorkspace(workspace)

    if ( 'errors' in response ):
        print(response['errors'][0]['detail'])
        return(1)
    
    return(0)
    


if __name__ == "__main__":
    cli()

