#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
    Copyright 2010 cloudControl UG (haftungsbeschraenkt)

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
"""

import argparse
import sys
from os import environ as env

import cctrl.settings as settings
from pycclib.cclib import *
from pycclib.version import __version__ as cclibversion
import cctrl.common as common
from cctrl.error import InputErrorException, messages
from cctrl.app import AppController, AppsController, ParseAppDeploymentName
from cctrl.auth import get_credentials, delete_tokenfile
from cctrl.output import get_version


class list_action(argparse.Action):
    """
        This action is needed by argparse because we use --list to get a list
        of apps that a user has access too. This is not directly supported by
        one of the other argpase actions. Therefore we needed to write our own.

        #TODO: Right now we do the error handling here and in common.run.
        It would be better to use common.run here too.
    """

    def __init__(self, api, **kwargs):
        super(list_action, self).__init__(**kwargs)
        self.api = api

    def __call__(self, parser, namespace, value, option_string=None):
        # check if there is a newer version of cctrl or pycclib
        try:
            common.check_for_updates(self.api.check_versions()['cctrl'])
        except KeyError:
            pass
        except ConnectionException:
            pass

        apps = AppsController(self.api)
        while True:
            try:
                try:
                    apps.list()
                except TokenRequiredError:
                    # check ENV for credentials first
                    try:
                        email = env.pop('CCTRL_EMAIL')
                        password = env.pop('CCTRL_PASSWORD')
                    except KeyError:
                        email, password = get_credentials()
                    try:
                        self.api.create_token(email, password)
                    except UnauthorizedError:
                        sys.exit(messages['NotAuthorized'])
                    else:
                        pass
                except ParseAppDeploymentName:
                    sys.exit(messages['InvalidAppOrDeploymentName'])
                else:
                    break
            except UnauthorizedError, e:
                if delete_tokenfile():
                    self.api.set_token(None)
                else:
                    sys.exit(messages['NotAuthorized'])
            except ForbiddenError, e:
                sys.exit(messages['NotAllowed'])
            except (ConnectionException, BadRequestError,
                    ConflictDuplicateError, GoneError, InternalServerError,
                    NotImplementedError, ThrottledError, InputErrorException), e:
                sys.exit(e)
        #noinspection PyUnreachableCode,PyUnboundLocalVariable
        parser.exit()


def main(api):
    app = AppController(api)
    parse_cmdline(app, api)


def parse_cmdline(app, api):
    """
        We parse the commandline using argparse from
        http://code.google.com/p/argparse/.

        argparse works with parsers and subparsers.

        Our main parser for cctrlapp always requires a name argument. We then
        add subparses for each action available for the app identified by name.

        Each subparser then may have it's own arguments needed for that
        particular subparsers action to work.
    """
    version = get_version(settings.VERSION, cclibversion)
    description = "%(prog)s controls your cloudControl apps"
    epilog = "And now you're in control"

    parser = argparse.ArgumentParser(description=description, epilog=epilog)

    list_apps = parser.add_argument_group(
        'list apps',
        'get a list of all your apps')
    list_apps.add_argument(
        '-l',
        '--list',
        action=list_action,
        nargs=0,
        api=api,
        help='list apps')

    control_apps = parser.add_argument_group(
        'control apps',
        'control app or deployment')

    control_apps.add_argument(
        'name',
        help='app_name for apps or app_name/deployment_name for deployments',
        metavar='app_name[/deployment_name]')

    subparsers = parser.add_subparsers(
        title='commands',
        description='available commands',
        metavar='')

    run_cmd_subparser = subparsers.add_parser(
        'run',
        help="run command")
    run_cmd_subparser.add_argument(
        'command',
        help='command to execute')
    run_cmd_subparser.set_defaults(func=app.run_cmd)

    create_subparser = subparsers.add_parser(
        'create',
        help="create new application")
    create_subparser.add_argument(
        'type',
        help='app-type (language)',
        metavar='type')
    create_subparser.add_argument(
        '--buildpack',
        help='custom buildpack URL (for "custom" app type)',
        default=None,
        metavar='url')
    create_subparser.add_argument(
        '--repo',
        choices=['bzr', 'git'],
        help='bzr or git',
        default=None,
        metavar='repo')
    create_subparser.set_defaults(func=app.create)

    details_subparser = subparsers.add_parser(
        'details',
        help="app or deployment details")
    details_subparser.set_defaults(func=app.details)

    delete_subparser = subparsers.add_parser(
        'delete',
        help="delete application")
    delete_subparser.add_argument(
        '-f',
        '--force',
        action="store_true",
        dest="force_delete",
        help="don't ask for confirmation")
    delete_subparser.set_defaults(func=app.delete)

    push_subparser = subparsers.add_parser('push', help="push local changes")
    push_subparser.add_argument(
        'source',
        nargs='?',
        help='path to local branch')
    push_subparser.set_defaults(func=app.push)

    deploy_subparser = subparsers.add_parser('deploy', help="deploy version")
    deploy_subparser.add_argument(
        '--containers', '--min',
        type=int,
        metavar='NUM',
        help="number of containers")
    deploy_subparser.add_argument(
        '--size', '--max',
        type=int,
        help="container memory size in units of 128MB")
    deploy_subparser.add_argument(
        '--memory',
        help="container size in MB, --memory=X*128 maps to --size=X")
    deploy_subparser.add_argument(
        '--stack',
        action="store",
        nargs='?',
        default=None,
        dest="stack",
        help="desired stack name")
    deploy_subparser.add_argument(
        'version',
        nargs='?',
        default=-1,
        help='optionally accepts a version')
    deploy_subparser.set_defaults(func=app.deploy)

    undeploy_subparser = subparsers.add_parser(
        'undeploy',
        help="delete deployment")
    undeploy_subparser.add_argument(
        '-f',
        '--force',
        action="store_true",
        dest="force_delete",
        help="don't ask for confirmation")
    undeploy_subparser.set_defaults(func=app.undeploy)

    showUser_subparser = subparsers.add_parser('user', help="list app users")
    showUser_subparser.set_defaults(func=app.showUser)

    addUser_subparser = subparsers.add_parser(
        'user.add',
        help="add user by email")
    addUser_subparser.add_argument('email')
    addUser_subparser.add_argument(
        '--role',
        action="store",
        nargs='?',
        default=None,
        dest="role",
        help="role")
    addUser_subparser.set_defaults(func=app.addUser)

    removeUser_subparser = subparsers.add_parser(
        'user.remove',
        help="remove user by username or email")
    removeUser_subparser.add_argument('username', help='username or email')
    removeUser_subparser.set_defaults(func=app.removeUser)

    showConfig_subparser = subparsers.add_parser('config', help="list config vars")
    showConfig_subparser.add_argument(
        'key',
        nargs='?',
        help='name of the key e.g. foo')
    showConfig_subparser.set_defaults(func=app.showConfig)

    addConfig_subparser = subparsers.add_parser(
        'config.add',
        help="add config vars")
    addConfig_subparser.add_argument(
        '-f',
        '--force',
        action="store_true",
        dest="force_add",
        help="don't ask for confirmation")
    addConfig_subparser.add_argument(
        'variables',
        nargs=argparse.REMAINDER,
        help='variables to add passed through to the config add-on e.g foo=bar baz=faa')
    addConfig_subparser.set_defaults(func=app.addConfig)

    removeConfig_subparser = subparsers.add_parser(
        'config.remove',
        help="remove config variables")
    removeConfig_subparser.add_argument(
        'variables',
        nargs=argparse.REMAINDER,
        help='variables to remove on the config add-on e.g. foo baz')
    removeConfig_subparser.set_defaults(func=app.removeConfig)

    showAddon_subparser = subparsers.add_parser('addon', help="show addon")
    showAddon_subparser.add_argument(
        'addon',
        nargs='?',
        help='name of the addon e.g. mysql.free')
    showAddon_subparser.set_defaults(func=app.showAddon)

    showAddon_subparser = subparsers.add_parser(
        'addon.list',
        help="list all available addons")
    showAddon_subparser.set_defaults(func=app.listAddons)

    showAddon_subparser = subparsers.add_parser(
        'addon.creds',
        help="print creds.json format to stdout")
    showAddon_subparser.add_argument(
        'addon',
        nargs='?',
        help='name of the addon e.g. mysql.free')
    showAddon_subparser.set_defaults(func=app.showAddonCreds)

    addAddon_subparser = subparsers.add_parser(
        'addon.add',
        help="add addon")
    addAddon_subparser.add_argument(
        'addon',
        help='name of the addon e.g. mysql.free')
    addAddon_subparser.add_argument(
        'options',
        nargs=argparse.REMAINDER,
        help='options passed through to the add-on - if path to file the content is sent')
    addAddon_subparser.set_defaults(func=app.addAddon)

    addAddon_subparser = subparsers.add_parser(
        'addon.upgrade',
        help="upgrade addon")
    addAddon_subparser.add_argument(
        'addon_old',
        help='current addon option')
    addAddon_subparser.add_argument(
        'addon_new',
        help='addon option to upgrade to')
    addAddon_subparser.set_defaults(func=app.updateAddon)

    addAddon_subparser = subparsers.add_parser(
        'addon.downgrade',
        help="downgrade addon")
    addAddon_subparser.add_argument(
        'addon_old',
        help='current addon option')
    addAddon_subparser.add_argument(
        'addon_new',
        help='addon option to downgrade to')
    addAddon_subparser.set_defaults(func=app.updateAddon)

    removeAddon_subparser = subparsers.add_parser(
        'addon.remove',
        help="remove addon")
    removeAddon_subparser.add_argument(
        'addon',
        help='name of the addon e.g. mysql.free')
    removeAddon_subparser.set_defaults(func=app.removeAddon)

    showAlias_subparser = subparsers.add_parser('alias', help="show alias")
    showAlias_subparser.add_argument(
        'alias',
        nargs='?',
        help='name of the alias e.g. www.example.com')
    showAlias_subparser.set_defaults(func=app.showAlias)

    addAlias_subparser = subparsers.add_parser('alias.add', help="add alias")
    addAlias_subparser.add_argument(
        'alias',
        help='name of the alias e.g. www.example.com')
    addAlias_subparser.set_defaults(func=app.addAlias)

    removeAlias_subparser = subparsers.add_parser(
        'alias.remove',
        help="remove alias")
    removeAlias_subparser.add_argument(
        'alias',
        help='name of the alias e.g. www.example.com')
    removeAlias_subparser.set_defaults(func=app.removeAlias)

    showWorker_subparser = subparsers.add_parser(
        'worker',
        help="show worker")
    showWorker_subparser.add_argument(
        'wrk_id',
        nargs='?',
        help='optional wrk_id for single worker details')
    showWorker_subparser.set_defaults(func=app.showWorker)

    addWorker_subparser = subparsers.add_parser(
        'worker.add',
        help="start worker")
    addWorker_subparser.add_argument(
        'command',
        help='the command to execute')
    addWorker_subparser.add_argument(
        'params',
        nargs='?',
        default=None,
        help='optional parameters to append to the command')
    addWorker_subparser.add_argument(
        '--size',
        type=int,
        help="container memory size in units of 128MB")
    addWorker_subparser.add_argument(
        '--memory',
        help="container size in MB, --memory=X*128 maps to --size=X")
    addWorker_subparser.set_defaults(func=app.addWorker)

    removeWorker_subparser = subparsers.add_parser(
        'worker.remove',
        help="stop worker")
    removeWorker_subparser.add_argument(
        'wrk_id',
        help='stop worker by wrk_id')
    removeWorker_subparser.set_defaults(func=app.removeWorker)

    showCron_subparser = subparsers.add_parser('cron', help="show cronjobs")
    showCron_subparser.add_argument(
        'job_id',
        nargs='?',
        help='optional job_id for single worker details')
    showCron_subparser.set_defaults(func=app.showCron)

    addCron_subparser = subparsers.add_parser('cron.add', help="add cronjob")
    addCron_subparser.add_argument('url', help='url to call')
    addCron_subparser.set_defaults(func=app.addCron)

    removeCron_subparser = subparsers.add_parser(
        'cron.remove',
        help="remove cronjob")
    removeCron_subparser.add_argument(
        'job_id',
        help='remove cronjob by job_id')
    removeCron_subparser.set_defaults(func=app.removeCron)

    log_subparser = subparsers.add_parser('log', help="show the log")
    log_subparser.add_argument(
        'type',
        choices=['access', 'error', 'worker', 'deploy'])
    log_subparser.add_argument(
        '-w',
        '--wrk_id',
        action="store",
        nargs='?',
        default=None,
        dest="wrk_id",
        help="a wrk_id to filter the worker log")
    log_subparser.add_argument(
        '-g',
        '--grep',
        action="store",
        nargs='?',
        default=None,
        dest="filter",
        help="pattern to search for within the log entry")
    log_subparser.set_defaults(func=app.log)

    parser.add_argument(
        '-v',
        '--version',
        action='version',
        version=version,
        help='show version string')

    open_subparser = subparsers.add_parser(
        'open',
        help="open deployment on default browser"
    )
    open_subparser.set_defaults(func=app.open)

    args = parser.parse_args()

    common.run(args, api)


if __name__ == "__main__":
    api = common.init_api()
    try:
        main(api)
    except KeyboardInterrupt:
        pass
    finally:
        common.shutdown(api)
