#!/usr/bin/env python
import argparse
import errno
import os
import subprocess

from collections import defaultdict

ITEMS = defaultdict(lambda: {'success': [], 'error': []})


class ValidationException(Exception):
    def __init__(self, error_msg, error_no=errno.EFAULT):
        self.errmsg = error_msg
        self.errno = error_no

    def get_error_name(self):
        return errno.errorcode.get(self.errno) or 'EUNKNOWN'

    def __str__(self):
        return f'[{self.get_error_name()}] {self.errmsg}'


class NotFoundException(ValidationException):
    def __init__(self, error):
        super().__init__(error, errno.ENOENT)


class TrainNotFoundException(NotFoundException):
    def __init__(self):
        super(TrainNotFoundException, self).__init__('Failed to find train')


class CatalogItemNotFoundException(NotFoundException):
    def __init__(self, path):
        super(CatalogItemNotFoundException, self).__init__(f'Failed to find {path!r} catalog item')


def report_result():
    print('[\033[94mINFO\x1B[0m]\tExecution Complete')
    for index, item in enumerate(ITEMS):
        index += 1
        data = ITEMS[item]
        print(f'\n\n[\033[94mINFO\x1B[0m]\t{index}) {item}')
        if data['success']:
            print(
                f'[\033[92mOK\x1B[0m]\t - Successfully updated dependencies for {", ".join(data["success"])} versions'
            )

        for i_v, version in enumerate(data['error']):
            v_name, error = version
            print(
                f'[\033[91mFAILED\x1B[0m]\t ({i_v + 1}) Failed to update dependencies for {v_name!r} version: {error}'
            )


def update_train_charts(train_path):
    # We will gather all charts in the train and then for each chart all it's versions will be updated
    if not os.path.exists(train_path):
        raise TrainNotFoundException()

    print(f'[\033[94mINFO\x1B[0m]\tProcessing {train_path!r} train')
    for item in os.listdir(train_path):
        process_catalog_item(os.path.join(train_path, item))

    report_result()


def process_catalog_item(item_path):
    if not os.path.exists(item_path):
        raise CatalogItemNotFoundException(item_path)

    item_name = item_path.rsplit('/', 1)[-1]
    print(f'[\033[94mINFO\x1B[0m]\tProcessing {item_name!r} catalog item')
    for item_version in os.listdir(item_path):
        update_item_version(item_name, item_version, os.path.join(item_path, item_version))


def update_item_version(item_name, version, version_path):
    cp = subprocess.Popen(
        ['helm', 'dependency', 'update', version_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE
    )
    stdout, stderr = cp.communicate()
    if cp.returncode:
        ITEMS[item_name]['error'].append((version, stderr.decode()))
    else:
        ITEMS[item_name]['success'].append(version)


def main():
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(help='sub-command help', dest='action')

    parser_setup = subparsers.add_parser('update', help='Update dependencies for specified train')
    parser_setup.add_argument('--train', help='Specify train path to update dependencies', required=True)

    args = parser.parse_args()
    if args.action == 'update':
        update_train_charts(args.train)
    else:
        parser.print_help()


if __name__ == '__main__':
    main()
