# Copyright 2016-2017 Capital One Services, LLC
# Copyright The Cloud Custodian Authors.
# SPDX-License-Identifier: Apache-2.0
import functools
from botocore.exceptions import ClientError

from concurrent.futures import as_completed

from c7n.actions import ActionRegistry, BaseAction
from c7n.filters import FilterRegistry, ValueFilter
from c7n.filters.iamaccess import CrossAccountAccessFilter
from c7n.manager import resources, ResourceManager
from c7n import query, utils
from c7n.utils import generate_arn, type_schema


ANNOTATION_KEY_MATCHED_METHODS = 'c7n:matched-resource-methods'
ANNOTATION_KEY_MATCHED_INTEGRATIONS = 'c7n:matched-method-integrations'


@resources.register('rest-account')
class RestAccount(ResourceManager):
    # note this is not using a regular resource manager or type info
    # its a pseudo resource, like an aws account

    filter_registry = FilterRegistry('rest-account.filters')
    action_registry = ActionRegistry('rest-account.actions')

    class resource_type(query.TypeInfo):
        service = 'apigateway'
        name = id = 'account_id'
        dimension = None
        arn = False

    @classmethod
    def get_permissions(cls):
        # this resource is not query manager based as its a pseudo
        # resource. in that it always exists, it represents the
        # service's account settings.
        return ('apigateway:GET',)

    @classmethod
    def has_arn(self):
        return False

    def get_model(self):
        return self.resource_type

    def _get_account(self):
        client = utils.local_session(self.session_factory).client('apigateway')
        try:
            account = client.get_account()
        except ClientError as e:
            if e.response['Error']['Code'] == 'NotFoundException':
                return []
        account.pop('ResponseMetadata', None)
        account['account_id'] = 'apigw-settings'
        return [account]

    def resources(self):
        return self.filter_resources(self._get_account())

    def get_resources(self, resource_ids):
        return self._get_account()


OP_SCHEMA = {
    'type': 'object',
    'required': ['op', 'path'],
    'additonalProperties': False,
    'properties': {
        'op': {'enum': ['add', 'remove', 'update', 'copy', 'replace', 'test']},
        'path': {'type': 'string'},
        'value': {'type': 'string'},
        'from': {'type': 'string'}
    }
}


@RestAccount.action_registry.register('update')
class UpdateAccount(BaseAction):
    """Update the cloudwatch role associated to a rest account

    :example:

    .. code-block:: yaml

        policies:
          - name: correct-rest-account-log-role
            resource: rest-account
            filters:
              - cloudwatchRoleArn: arn:aws:iam::000000000000:role/GatewayLogger
            actions:
              - type: update
                patch:
                  - op: replace
                    path: /cloudwatchRoleArn
                    value: arn:aws:iam::000000000000:role/BetterGatewayLogger
    """

    permissions = ('apigateway:PATCH',)
    schema = utils.type_schema(
        'update',
        patch={'type': 'array', 'items': OP_SCHEMA},
        required=['patch'])

    def process(self, resources):
        client = utils.local_session(
            self.manager.session_factory).client('apigateway')
        client.update_account(patchOperations=self.data['patch'])


class ApiDescribeSource(query.DescribeSource):

    def augment(self, resources):
        for r in resources:
            tags = r.setdefault('Tags', [])
            for k, v in r.pop('tags', {}).items():
                tags.append({
                    'Key': k,
                    'Value': v})
        return resources


@resources.register('rest-api')
class RestApi(query.QueryResourceManager):

    class resource_type(query.TypeInfo):
        service = 'apigateway'
        arn_type = '/restapis'
        enum_spec = ('get_rest_apis', 'items', None)
        id = 'id'
        name = 'name'
        date = 'createdDate'
        dimension = 'GatewayName'
        cfn_type = config_type = "AWS::ApiGateway::RestApi"
        universal_taggable = object()
        permissions_enum = ('apigateway:GET',)

    source_mapping = {
        'config': query.ConfigSource,
        'describe': ApiDescribeSource
    }

    @property
    def generate_arn(self):
        """
         Sample arn: arn:aws:apigateway:us-east-1::/restapis/rest-api-id
         This method overrides c7n.utils.generate_arn and drops
         account id from the generic arn.
        """
        if self._generate_arn is None:
            self._generate_arn = functools.partial(
                generate_arn,
                self.resource_type.service,
                region=self.config.region,
                resource_type=self.resource_type.arn_type)
        return self._generate_arn


@RestApi.filter_registry.register('cross-account')
class RestApiCrossAccount(CrossAccountAccessFilter):

    policy_attribute = 'policy'
    permissions = ('apigateway:GET',)


@RestApi.action_registry.register('update')
class UpdateApi(BaseAction):
    """Update configuration of a REST API.

    Non-exhaustive list of updateable attributes.
    https://docs.aws.amazon.com/apigateway/api-reference/link-relation/restapi-update/#remarks

    :example:

    contrived example to update description on api gateways

    .. code-block:: yaml

       policies:
         - name: apigw-description
           resource: rest-api
           filters:
             - description: empty
           actions:
             - type: update
               patch:
                - op: replace
                  path: /description
                  value: "not empty :-)"
    """
    permissions = ('apigateway:PATCH',)
    schema = utils.type_schema(
        'update',
        patch={'type': 'array', 'items': OP_SCHEMA},
        required=['patch'])

    def process(self, resources):
        client = utils.local_session(
            self.manager.session_factory).client('apigateway')
        for r in resources:
            client.update_rest_api(
                restApiId=r['id'],
                patchOperations=self.data['patch'])


@RestApi.action_registry.register('delete')
class DeleteApi(BaseAction):
    """Delete a REST API.

    :example:

    contrived example to delete rest api

    .. code-block:: yaml

       policies:
         - name: apigw-delete
           resource: rest-api
           filters:
             - description: empty
           actions:
             - type: delete
    """
    permissions = ('apigateway:DELETE',)