#!/usr/bin/env python
"""Module with S3 bucket."""
from __future__ import print_function

from troposphere import (
    Equals, GetAtt, If, Join, Or, Output, Ref, Tags, s3
)

import awacs.s3
from awacs.aws import Condition, Deny, Principal, Policy, Statement
# autogenerated conditions trip up pylint
from awacs.aws import Null, StringNotEquals  # pylint: disable=E0611

from stacker.blueprints.base import Blueprint
from stacker.blueprints.variables.types import CFNString


class Bucket(Blueprint):
    """Stacker blueprint for creating S3 bucket."""

    VARIABLES = {
        'ApplicationName': {'type': CFNString,
                            'description': 'Application name'},
        'BucketName': {'type': CFNString,
                       'description': 'Name of bucket',
                       'default': ''},
        'CustomerName': {'type': CFNString,
                         'description': 'The nickname for the new customer. '
                                        'Must be all lowercase letters, '
                                        'should not contain spaces or special '
                                        'characters, nor should it include '
                                        'any part of EnvironmentName.',
                         'allowed_pattern': '[-_ a-z]*',
                         'default': ''},
        'EnvironmentName': {'type': CFNString,
                            'description': 'Name of Environment',
                            'default': 'common'}
    }

    def create_template(self):
        """Create template (main function called by Stacker)."""
        template = self.template
        variables = self.get_variables()
        template.add_version('2010-09-09')
        template.add_description('Sample app')

        # Conditions
        template.add_condition(
            'BucketNameOmitted',
            Or(Equals(variables['BucketName'].ref, ''),
               Equals(variables['BucketName'].ref, 'undefined'))
        )

        # Resources
        bucket = template.add_resource(
            s3.Bucket(
                'Bucket',
                AccessControl=s3.Private,
                BucketName=If(
                    'BucketNameOmitted',
                    Ref('AWS::NoValue'),
                    variables['BucketName'].ref
                ),
                LifecycleConfiguration=s3.LifecycleConfiguration(
                    Rules=[
                        s3.LifecycleRule(
                            NoncurrentVersionExpirationInDays=90,
                            Status='Enabled'
                        )
                    ]
                ),
                Tags=Tags(
                    application=variables['ApplicationName'].ref,
                    customer=variables['CustomerName'].ref,
                    environment=variables['EnvironmentName'].ref
                ),
                VersioningConfiguration=s3.VersioningConfiguration(
                    Status='Enabled'
                )
            )
        )
        template.add_output(Output(
            'BucketName',
            Description='Name of bucket',
            Value=Ref(bucket)
        ))
        template.add_output(Output(
            'BucketArn',
            Description='Arn of bucket',
            Value=GetAtt(bucket, 'Arn')
        ))

        # https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingServerSideEncryption.html
        template.add_resource(
            s3.BucketPolicy(
                'RequireBucketEncryption',
                Bucket=Ref(bucket),
                PolicyDocument=Policy(
                    Version='2012-10-17',
                    Statement=[
                        Statement(
                            Sid='DenyIncorrectEncryptionHeader',
                            Action=[awacs.s3.PutObject],
                            Condition=Condition(
                                StringNotEquals(
                                    's3:x-amz-server-side-encryption',
                                    'AES256'
                                )
                            ),
                            Effect=Deny,
                            Principal=Principal('*'),
                            Resource=[
                                Join('', [GetAtt(bucket, 'Arn'),
                                          '/*'])
                            ]
                        ),
                        Statement(
                            Sid='DenyUnEncryptedObjectUploads',
                            Action=[awacs.s3.PutObject],
                            Condition=Condition(
                                Null(
                                    's3:x-amz-server-side-encryption',
                                    'true'
                                )
                            ),
                            Effect=Deny,
                            Principal=Principal('*'),
                            Resource=[
                                Join('', [GetAtt(bucket, 'Arn'),
                                          '/*'])
                            ]
                        )
                    ]
                )
            )
        )


# Helper section to enable easy blueprint -> template generation
# (just run `python <thisfile>` to output the json)
if __name__ == "__main__":
    from stacker.context import Context
    print(Bucket('test',
                 Context({"namespace": "test"}),
                 None).to_json())
