#!/usr/bin/env python

import time
import argparse, argcomplete
import json
import sys
from collections import OrderedDict
import os

from servicemanager.serviceresolver import ServiceResolver
from servicemanager.smstatus import dostatus
from servicemanager.smcontext import SmApplication, SmContext, ServiceManagerException
from servicemanager.smrepo import pull_rebase_repo
from servicemanager.actions import actions
from servicemanager import smrepo

def ServiceCompleter(prefix, **kwargs):
    parsed_args = kwargs["parsed_args"]
    application = SmApplication(configuration_dir_parameter=parsed_args.config, features=parsed_args.feature)
    service_resolver = ServiceResolver(application)
    return service_resolver.resolve_services(prefix + "*")

def _process_command():

    parser = argparse.ArgumentParser(description='Service Manager - Start, Stop and View services')
    parser.add_argument('-r', '--release', type=str, nargs="?", const="LATEST", help='Used with start to run services using the specified release version, or the latest if unspecified')
    parser.add_argument('-f', '--fatjar', action='store_true', help='Used with start to run services using fatjars')
    parser.add_argument('-F', '--feature', action='append', help='Enable a given feature for all services started')
    parser.add_argument('-s', '--status', nargs='*', help='Get the status of a single service or a list of services')
    parser.add_argument('--autorefresh', nargs='*', help='Get the status of a single service or a list of services and refreshes until killed')
    parser.add_argument('-n', '--shownotrunning', action='store_true', help='Used with status command to show services that are not running')
    parser.add_argument('--proxy', type=str, help='Use with start to start those items with a proxy, i.e. add at the end --proxy localhost:8888')
    parser.add_argument('-o', '--offline', action='store_true', help='Offline mode: uses existing binaries instead of looking for an updated version online')
    parser.add_argument('--start', type=str, nargs='*', help='Starts a single service/profile, or a list of services/profiles').completer = ServiceCompleter
    parser.add_argument('--restart', type=str, nargs='*', help='Restarts a single service/profile or a list of services/profiles').completer = ServiceCompleter
    parser.add_argument('--stop', type=str, nargs='*', help='Stops a single service by id').completer = ServiceCompleter
    parser.add_argument('--pullall', action='store_true', help='Pulls all repos that you currently have')
    parser.add_argument('--noprogress', action='store_true', help='Stops file download progress from being displayed')
    parser.add_argument('--cleanlogs', action='store_true', help='Deletes the log files for all services')
    parser.add_argument('-w', '--wait', type=int, help='Wait at most the given number of seconds for services to pass healthchecks before returning')
    parser.add_argument('-d', '--describe',  action='store_true', help='Gives an outline of available services and profiles')
    parser.add_argument('-i', '--info', action='store_true', help='Shows info for started services, including git revision (if available) and java options')
    parser.add_argument('--ports', action='store_true', help='Shows a list of used default ports and their services')
    parser.add_argument('--port', type=int, help='Overrides the default http port the service is started on. Ignored if start is used with more than one service')
    parser.add_argument('--checkports', action='store_true', help='Validates default ports for duplicates - returns code 1 if duplicates exist')
    parser.add_argument('--printconfig', type=str, nargs='*', help='Print the config for a given list of services, if empty, show all')
    parser.add_argument('-c', '--config', type=str, help='Sets the configuration directory location, defaults to $WORKSPACE/service-manager-config')
    parser.add_argument('--getdascode', nargs='*', help='With no arguements, checks out all services if you haven\'t already. Else checks out just the specified service or profile.')
    parser.add_argument('--showcmdfor', type=str, nargs='*', help='Shows how sm will try start the service')
    parser.add_argument('-l', '--logs', type=str, help='View the logs associated with the service').completer = ServiceCompleter

    argcomplete.autocomplete(parser)

    args = parser.parse_args()

    show_progress = not args.noprogress

    application = SmApplication(configuration_dir_parameter=args.config, features=args.feature)
    context = SmContext(application, None, args.offline, show_progress)
    service_resolver = ServiceResolver(application)

    # Actions
    if args.stop:
        for service_name in service_resolver.resolve_services_from_array(args.stop):
            if context.has_service(service_name):
                context.kill(service_name)
            elif context.is_profile(service_name):
                actions.stop_profile(context, service_name)
            else:
                print "The requested service %s does not exist" % service_name

    if args.status is not None:
            dostatus(context, args.status, args.shownotrunning)

    if args.autorefresh is not None:
        while True:
            dostatus(context, args.autorefresh, args.shownotrunning, clear_before_print=True)
            time.sleep(3)

    if args.describe:
        context.application.describe()

    if args.restart:
        for service_name in service_resolver.resolve_services_from_array(args.restart):
            if service_name not in context.application.services:
                print "The requested service %s does not exist" % service_name
            else:
                context.kill(service_name)
        time.sleep(5)
        for service_name in service_resolver.resolve_services_from_array(args.restart):
            if context.has_service(service_name):
                actions.start_one(context, service_name, args.fatjar, args.release, args.proxy, actions.overridden_port(args.restart, args.port))

    if args.start:
        actions.start_and_wait(service_resolver, context, args.start, args.fatjar, args.release, args.proxy, args.port, args.wait)

    if args.cleanlogs:
        for service_name in context.application.services:
            actions.clean_logs(context, service_name)

    if args.printconfig is not None:
        if len(args.printconfig) > 0:
            for service_name in args.printconfig:
                print json.dumps(context.application.services[service_name], indent=2, separators=(',', ': '))
        else:
            print json.dumps(context.application.services, indent=2, separators=(',', ': '))

    if args.info:
        for service_name in context.application.services:
            actions.display_info(context, service_name)
    
    if args.pullall:
        for service_name in context.application.services:
            pull_rebase_repo(context, service_name, context.application.services[service_name])

    if args.ports:
        ports_used = context.get_ports_used()
        for port in OrderedDict(sorted(ports_used.items(), key=lambda t: t[0])):
            print str(port) + " -> " + ports_used[port]

    if args.checkports:
        ports_used = {}
        for service_name in context.application.services:
            service = context.application.services[service_name]
            if "defaultPort" in service:
                if service["defaultPort"] in ports_used:
                    SmApplication.exit_with_error("Duplicate port found : " + str(service["defaultPort"]) +
                                                  " in services: " + service_name + " and " + ports_used[service["defaultPort"]])
                ports_used[service["defaultPort"]] = service_name
            if "defaultAdminPort" in service:
                if service["defaultAdminPort"] in ports_used:
                    SmApplication.exit_with_error("Duplicate port found : " + str(service["defaultAdminPort"]) +
                                                  " in services: " + service_name + " and " + ports_used[service["defaultAdminPort"]])
                    ports_used[service["defaultAdminPort"]] = service_name

    if args.getdascode is not None:
        services = context.application.services
        if len(args.getdascode) > 0:
            services = service_resolver.resolve_services_from_array(args.getdascode)
        for service_name in services:
            service_data = context.application.services[service_name]
            if "sources" in service_data and "location" in service_data and "repo" in service_data["sources"]:
                path = context.application.workspace + service_data["location"]
                smrepo.clone_repo_if_required_raw(service_name, service_data["sources"]["repo"], path, context)

    if args.showcmdfor:
        print "The parameters you provided would start the following services using the following commands:"
        for service_name in service_resolver.resolve_services_from_array(args.showcmdfor):
            if context.has_service(service_name):
                cmd = actions.get_start_cmd(context, service_name, args.fatjar, args.release, args.proxy, actions.overridden_port(args.showcmdfor, args.port))
                print service_name
                print " ".join(cmd)
            else:
                print "The requested service %s does not exist" % service_name

    if args.logs:
        if context.has_service(args.logs):
            logfile = actions.get_log_file(context, args.logs)
            pager = os.environ.get("PAGER")
            if pager:
                args = pager.split()[1:]
                pager = pager.split()[0]
            else:
                pager = "less"
                args = ["+G","-R"] # Start at the end of the file, and display binary characters properly.
            args.insert(0, pager)
            args.append(logfile)
            os.execvp(pager, args)
        else:
            print "The requested service %s does not exist" % args.logs

try:
    _process_command()
except ServiceManagerException as e:
    print e.message
    sys.exit(-1)
