# pylint: disable=too-many-locals
"""
awstaga
=======
Tag any AWS resource via config file.
"""

import time
import json
import boto3
import click
from .config import load
from .logger import init

def apply(conf_file: str, dry_run: bool, batch_size: int, delay: int) -> None:
    """Apply tags to resources based on configuration file."""

    logger = init(dry_run)

    logger.info(f'Loading configuration file {conf_file}')
    conf = load(conf_file, dry_run)
    tagsets = conf[0]
    resources = conf[1]

    client = boto3.client('resourcegroupstaggingapi')

    batches = {}

    for resource in resources:
        tags = {}

        for tagset_name in resource.get_tagset_names():
            tagset = tagsets[tagset_name]
            for tagset_tag in tagset.get_tags():
                tags[tagset_tag.get_key()] = tagset_tag.get_value()

        for resource_tag in resource.get_tags():
            tags[resource_tag.get_key()] = resource_tag.get_value()

        if not tags:
            logger.warning(f'No tags to apply to resource {resource.get_arn()}')
        else:
            # Create a batch of resources with the same tags
            # the batch ID is derived from the tags dump.
            # Tags are purposely not sorted in order to distinguish
            # varying tag orders as defined by the users.
            batch_id = json.dumps(tags)
            if batch_id not in batches:
                batches[batch_id] = {'resource_arns': [], 'tags': tags}
            batches[batch_id]['resource_arns'].append(resource.get_arn())

            logger.info(f'Adding resource {resource.get_arn()} to a batch with tags {tags}')

            # Apply tags to current batch if batch_size is reached,
            # and the batch is then removed.
            if len(batches[batch_id]['resource_arns']) == batch_size:
                _process_batch(dry_run, delay, logger, client, batches[batch_id])
                batches.pop(batch_id)

    # Apply tags to remaining batches, each batch would have less than batch_size resources.
    for batch_id, batch in batches.items():
        _process_batch(dry_run, delay, logger, client, batch)

def _process_batch(dry_run: bool, delay: int, logger, client, batch: dict) -> None:
    if dry_run is False:
        logger.info(f'Applying {len(batch["resource_arns"])} resource(s) '\
                    f'with tags {batch["tags"]}')
        response = client.tag_resources(
                ResourceARNList=batch['resource_arns'],
                Tags=batch['tags']
            )
        if 'FailedResourcesMap' in response and len(response["FailedResourcesMap"].keys()) > 0:
            logger.error('Failed to apply tags to '\
                         f'{len(response["FailedResourcesMap"].keys())} resource(s):')
            for resource_arn, error in response['FailedResourcesMap'].items():
                logger.error(f'{resource_arn}: '\
                             f'{error.get("StatusCode")} - '\
                             f'{error.get("ErrorCode")} - '\
                             f'{error.get("ErrorMessage")}')
        time.sleep(delay)

@click.command()
@click.option('--conf-file', default='awstaga.yaml', show_default=True, type=str,
              help='Configuration file path')
@click.option('--dry-run', is_flag=True, default=False, show_default=True, type=bool,
              help='When dry run is enabled, no tags are applied')
@click.option('--batch-size', default=5, show_default=True, type=int,
              help='Number of resources to tag in one go per batch')
@click.option('--delay', default=2, show_default=True, type=int,
              help='Delay in seconds after tagging each batch')
def cli(conf_file: str, dry_run: bool, batch_size: int, delay: int) -> None:
    """Python CLI for tagging AWS resources based on a YAML configuration.
    """
    apply(conf_file, dry_run, batch_size, delay)
