"""
    This behavior file contains the logic for a subset of commands. Logic specific to
    commands should be implamented in corresponding behavior files.
"""

from pathlib import Path
from cli import pyke
import requests
import click
import time
import json
import sys
import os

class WidgetVersionBehavior:
    def __init__(self, profile=None, widget=None, format=None, filter=None, page=None, pagesize=None, json=False):
        self.util = pyke.Util(profile=profile)
        if profile is not None:
            self.context = pyke.auth.load_cache()
        self.object_type = 'widget'
        self.json = json
        self.profile = profile
        self.widget = widget
        self.format = format
        self.filter = filter
        self.page = page
        self.pagesize = pagesize

    def list(self, widget):
        widget_id = self.util.lookup_object_id(self.object_type, widget)
        data = {'filter': self.filter, 'page': self.page, 'pagesize': self.pagesize}

        handlers = {
          'actualState': lambda x: click.style(x, fg='red') if x not in ['running'] else click.style(x, fg='green')
        }

        resp = self.util.cli_request('GET',
            self.util.build_url('{app}/iot/v1/containers/{widget_id}/versions?page={page}&pagesize={pagesize}&{filter}',
            {'widget_id': widget_id, **data}))

        if self.json:
            click.echo(json.dumps(resp))
            return

        self.util.print_table(resp['payload']['data'], self.format, handlers=handlers)
        self.util.print_record_count(resp)

    def start_stop(self, action, widget, version):
        widget_id = self.util.lookup_object_id(self.object_type, widget)
        version_data = None

        try:
            version_id = int(version)
        except:
            major, minor, patch = self.util.parse_version_number(version)

            version_data = self.util.cli_request('GET', self.util.build_url('{app}/iot/v1/containers/{widget_id}/versions?major={major}&minor={minor}&patch={patch}',\
                { 'widget_id': widget_id, 'major': major, 'minor': minor, 'patch': patch }))['payload']['data']

            if version_data is None or len(version_data) < 1:
                raise click.ClickException(click.style("Version not found", fg='red'))
            else:
                version_id = version_data[0].get('id')
                current_state = version_data[0].get('actualState')

        if version_data is None:
            version_data = self.util.cli_request('GET', self.util.build_url('{app}/iot/v1/containers/{widget_id}/versions/{version_id}',\
                {'widget_id': widget_id, 'version_id': version_id} ))['payload']['data']

            if version_data is None or len(version_data) < 1:
                raise click.ClickException(click.style("Version not found", fg='red'))

            current_state = version_data.get('actualState')

        if current_state not in ['stopped', 'running', 'error']:
            raise click.ClickException(click.style("To start or stop a version it must have a 'stopped', 'running' or 'error' state. The current state of this version is {}. \
                A version goes through several states after being uploaded to Lumavate. It must finish this process before this command can be run. \
                To check its current state, run 'luma widget-version ls'.".format(current_state), fg='yellow'))

        if action == 'stop' and current_state != 'running':
            raise click.ClickException(click.style("This version is not running", fg='yellow'))
        if action == 'start' and current_state == 'running':
            raise click.ClickException(click.style("This version is already running", fg='yellow'))

        titles = {
            'id': 'id',
            'actualState': 'Current State',
            'versionNumber': 'Version #',
            'createdAt': 'Created At',
            'createdBy': 'Created By'
        }

        data = {
            "action": action,
            "containerId": widget_id,
            "id": version_id
        }

        resp = self.util.cli_request('POST', self.util.build_url('{app}/iot/v1/containers/{widget_id}/versions/{version_id}/{action}',\
            {'widget_id': widget_id, 'version_id': version_id, 'action': action}), data=data)

        if self.json:
            click.echo(json.dumps(resp))
            return

        status_id = resp['payload']['data']['results']['statusId']
        progress = 0
        error = None
        if action == 'start':
            bar_name = "Starting widget"
        else:
            bar_name = "Stopping widget"

        with click.progressbar(length=100, label=bar_name) as bar:
            while progress < 100 and error is None:
                status_resp = self.util.cli_request('GET', self.util.build_url('{app}/iot/v1/statuses/{status_id}', {'status_id': status_id}))
                progress = status_resp['payload']['data']['overallPercent']
                error = status_resp['payload']['data']['errorMessage']
                bar.update(progress)

        click.echo(' ')
        ver_resp = self.util.cli_request('GET', self.util.build_url('{app}/iot/v1/containers/{widget_id}/versions/{version_id}', {'widget_id': widget_id, 'version_id': version_id}))

        handlers = {
          'actualState': lambda x: click.style(x, fg='red') if x not in ['running'] else click.style(x, fg='green')
        }

        self.util.print_table([ver_resp['payload']['data']], self.format, handlers=handlers, titles=titles)

    def run(self, widget, version, command, target):
        widget_id = self.util.lookup_object_id(self.object_type, widget)
        version_id = self.util.get_version_id(self.object_type, widget_id, version)

        data = {
            "command": command,
            "target": target
        }

        resp = self.util.cli_request('POST', self.util.build_url('{app}/iot/v1/containers/{widget_id}/versions/{version_id}/exec',\
            {'widget_id': widget_id, 'version_id': version_id}), json=data)

        if self.json:
            click.echo(json.dumps(resp))
            return

        status_id = resp['payload']['data']['statusId']
        progress = 0
        error = None
        with click.progressbar(length=100, label='Executing command') as bar:
            while progress < 100:
                status_resp = self.util.cli_request('GET', self.util.build_url('{app}/iot/v1/statuses/{status_id}', {'status_id': status_id}))
                progress = status_resp['payload']['data']['overallPercent']
                bar.update(progress)
                if status_resp['payload']['data']['errorMessage'] is not None:
                    click.echo('There was an error running the command')
                    raise click.ClickException('Status: {}'.format(status_resp['payload']['data']))

        click.echo(' ')
        click.echo('Command Output: ')
        click.echo(' ')
        try:
            status_resp = self.util.cli_request('GET', self.util.build_url('{app}/iot/v1/statuses/{status_id}', {'status_id': status_id}))['payload']['data']
            if target == 'one':
                summary = status_resp['summary']['results']
                if 'output' in summary[0].keys():
                    if isinstance(summary[0]['output'], list):
                        for x in summary[0]['output']:
                            click.echo(x)
                    else:
                        click.echo(summary)
            else:
                summary = status_resp['summary']['results']
                for x in summary:
                    click.echo(x)

        except:
            click.echo(status_resp)

    def logs(self, widget, version):
        widget_id = self.util.lookup_object_id(self.object_type, widget)
        version_id = self.util.get_version_id(self.object_type, widget_id, version)

        resp = self.util.cli_request('GET', self.util.build_url('{app}/iot/v1/containers/{widget_id}/versions/{version_id}/logs',\
            {'widget_id': widget_id, 'version_id': version_id}))

        if self.json:
            click.echo(json.dumps(resp))
            return

        for x in resp['payload']['data']:
            click.echo(x)

    def create(self, widget, port, widget_file_path, docker_image,\
        from_version, version_number, version_bump, env, label, format):
        widget_id = self.util.lookup_object_id(self.object_type, widget)

        if docker_image is not None and docker_image != '':
            if not self.json:
                click.echo("Getting docker image ")
            cu = pyke.ContainerUtil()
            # save_image() returns the file path of the zipped docker image
            widget_file_path = cu.save_image(docker_image)

        if from_version != '' and from_version is not None:
            try:
                from_version_id = int(from_version)
                version = self.util.cli_request('GET', self.util.build_url('{app}/iot/v1/containers/{widget_id}/versions/{from_version_id}',\
                    {'widget_id': widget_id, 'from_version_id': from_version_id} ))['payload']['data']
            except:
                version = self.util.get_version(self.object_type, widget_id, from_version)

            if version is None:
                raise click.BadParameter(click.style("Widget version not found", fg='red'), param_hint="--version-number")

            if version_number is not None and version_number != '':
                major, minor, patch = self.util.parse_version_number(version_number)

                version['major'] = major
                version['minor'] = minor
                version['patch'] = patch

            else:
                version[version_bump] = version[version_bump] + 1
                if version_bump in ['major', 'minor']:
                    version['patch'] = 0
                if version_bump == 'major':
                    version['minor'] = 0

        else:
            version = {}
            if label is None or label == '':
                label = input("Label: ")
                if label is None or label == '':
                    raise click.BadParameter(click.style("If you don't create from an old version then you must provide a --label", fg='red'), param_hint="--label")

            if version_number is None or version_number == '':
                version_number = input("Version Number: ")
                major, minor, patch = self.util.parse_version_number(version_number)

                version['major'] = major
                version['minor'] = minor
                version['patch'] = patch

                if version_number is None or version_number == '':
                    raise click.BadParameter(click.style("If you don't create from an old version then you must provide a --version-number in the format\
                        '<major: int>.<minor: int>.<patch: int>' ", fg='red'), param_hint="--version-number")

            if widget_file_path is None or widget_file_path == '':
                widget_file_path = input("Widget File Path: ").replace(" ", "")
                if widget_file_path is None or widget_file_path == '':
                    raise click.ClickException(click.style("If you don't create from an old version then you must provide --widget-file-path or --docker-image", fg='red'))

            if port is None or port == '':
                port = int(input("Port: "))
                if port is None or port == '':
                    raise click.ClickException(click.style("If you don't create from an old version then you must provide a --port for your widget", fg='red'))

            else:
                major, minor, patch = self.util.parse_version_number(version_number)

                version['major'] = major
                version['minor'] = minor
                version['patch'] = patch

        if widget_file_path is not None and widget_file_path != '':
            if not self.json:
                click.echo("Uploading image to Lumavate")
                click.echo('Image Size: {}'.format(self.util.get_size(widget_file_path)))

            version['tag'] = None
            version['ephemeralKey'] = self.util.upload_ephemeral(widget_file_path, 'application/gz')

        version['platformVersion'] = 'v2'
        if 'env' not in version.keys():
            version['env'] = {}

        for var in env:
            try:
                env_dict = json.loads(var)
                version['env'] = { **version['env'], **env_dict}
            except Exception as e:
                print("Could not serialize {}".format(var))
                print(e)

        if label != '' and label is not None:
            version['label'] = label

        if port != '' and port is not None:
            version['port'] = port

        version['instanceCount'] = 1

        resp = self.util.cli_request('POST',
            self.util.build_url('{app}/iot/v1/containers/{widget_id}/versions', {'widget_id': widget_id}), data=json.dumps(version))

        if self.json:
            click.echo(json.dumps(resp))
            return

        # TO DO: Add progress bar after create for loading/validating widget
        progress = 0
        status_id = None
        error = None

        if 'results' in resp['payload']['data'].keys():
            status_id = resp['payload']['data']['results']['statusId']
        if status_id:
            with click.progressbar(length=100, label='Creating widget') as bar:
                while progress < 100 and error is None:
                    status_resp = self.util.cli_request('GET', self.util.build_url('{app}/iot/v1/statuses/{status_id}', {'status_id': status_id}))
                    progress = status_resp['payload']['data']['overallPercent']
                    error = status_resp['payload']['data']['errorMessage']
                    bar.update(progress)


        handlers = {
            'actualState': lambda x: click.style(x, fg='red') if x not in ['running'] else click.style(x, fg='green')
        }

        self.util.print_table([resp['payload']['data']], format, handlers=handlers)

        if docker_image is not None and docker_image != '':
            try:
                os.remove(widget_file_path)
            except:
                click.echo('Failed to pick up our mess...')

    def update(self, widget, version, label, format):
        widget_id = self.util.lookup_object_id(self.object_type, widget)
        version_id = self.util.get_version_id(self.object_type, widget_id, version)

        post_data = {
          'widgetId': widget_id,
          'id': version_id,
          'label': label
        }

        resp = self.util.cli_request('PUT',\
            self.util.build_url('{app}/iot/v1/containers/{widget_id}/versions/{version_id}',
            {'widget_id': widget_id, 'version_id': version_id}), data=json.dumps(post_data))

        if self.json:
            click.echo(json.dumps(resp))
            return

        self.util.print_table([resp['payload']['data']], format)

    def delete(self, widget, version, version_mask, format):
        if version is None and version_mask is None:
            version = str(input("Version: "))
            if version is None or version == '':
                raise click.BadParameter(click.style('You must provide either a --version or a --version-mask', fg='red'))

        widget_id = self.util.lookup_object_id(self.object_type, widget)

        if version is not None:
            version_id = self.util.get_version_id(self.object_type, widget_id, version)

            resp = self.util.cli_request('DELETE',\
                self.util.build_url('{app}/iot/v1/containers/{widget_id}/versions/{version_id}', {'widget_id': widget_id, 'version_id': version_id}))

            if self.json:
                click.echo(json.dumps(resp))
                return

            self.util.print_table([resp['payload']['data']], format)

        else:
            versions = self.util.get_versions_from_mask(self.object_type, widget_id, version_mask)

            if self.json:
                resp_list = {}
                resp_list['responses'] = []
                for v in versions:
                    resp = self.util.cli_request('DELETE',
                        self.util.build_url('{app}/iot/v1/containers/{widget_id}/versions/{version_id}', {'widget_id': widget_id, 'version_id': v['id']}))
                    resp_list['responses'].append(resp)
                click.echo(json.dumps(resp_list))
                return

            with click.progressbar(versions, label='Deleting Versions') as bar:
                for v in bar:
                    resp = self.util.cli_request('DELETE',
                        self.util.build_url('{app}/iot/v1/containers/{widget_id}/versions/{version_id}', {'widget_id': widget_id, 'version_id': v['id']}))

            self.util.print_table(versions, format)
