from troposphere import (
    sso,
    rds,
    ec2,
    elasticloadbalancing,
    elasticloadbalancingv2,
    validators,
    AWSHelperFn,
    If,
    Parameter,
    Tags,
)


# Temporary fix for https://github.com/cloudtools/troposphere/issues/2146
sso.PermissionSet.props["InlinePolicy"] = (str, False)


# set Parameter default values
def my_parameter_init(self, title, **kwargs):
    super(Parameter, self).__init__(title, **kwargs)
    if "Type" not in kwargs:
        self.Type = "String"
    if "Default" not in kwargs:
        self.Default = ""


Parameter.__init__ = my_parameter_init


# Temporary fix for https://github.com/cloudtools/troposphere/issues/1474
def my_one_of(class_name, properties, property, conditionals):
    if properties.get(property) not in conditionals and not isinstance(
        properties.get(property), If
    ):
        raise ValueError(
            # Ensure we handle None as a valid value
            '%s.%s must be one of: "%s"'
            % (
                class_name,
                property,
                ", ".join(condition for condition in conditionals if condition),
            )
        )


elasticloadbalancingv2.one_of = my_one_of

# Fix troposphere/elasticloadbalancing.py LBCookieStickinessPolicy is a List and do not use class LBCookieStickinessPolicy
elasticloadbalancing.LoadBalancer.props["LBCookieStickinessPolicy"] = (
    [elasticloadbalancing.LBCookieStickinessPolicy],
    False,
)
elasticloadbalancing.LoadBalancer.props["Listeners"] = (
    [elasticloadbalancing.Listener],
    False,
)


# Fix to avoid setting OS variable TROPO_REAL_BOOL
def boolean(x):
    if x in [True, 1, "1", "true", "True"]:
        return True
    if x in [False, 0, "0", "false", "False"]:
        return False
    raise ValueError


validators.boolean = boolean


# RDSDBInstance reax check when using SourceDBInstanceIdentifier, forbidden properties can use If Condition to remove them
def validate_dbinstance(self):
    if "DBSnapshotIdentifier" not in self.properties:
        if "Engine" not in self.properties:
            raise ValueError(
                "Resource Engine is required in type %s" % self.resource_type
            )

    #    if "SourceDBInstanceIdentifier" in self.properties:
    #
    #        invalid_replica_properties = (
    #            "DBName",
    #            "MasterUsername",
    #            "MasterUserPassword",
    #            "PreferredBackupWindow",
    #            "MultiAZ",
    #            "DBSnapshotIdentifier",
    #        )
    #
    #        invalid_properties = [
    #            s for s in self.properties.keys() if s in invalid_replica_properties
    #        ]
    #
    #        if invalid_properties:
    #            raise ValueError(
    #                (
    #                    "{0} properties can't be provided when "
    #                    "SourceDBInstanceIdentifier is present "
    #                    "AWS::RDS::DBInstance."
    #                ).format(", ".join(sorted(invalid_properties)))
    #            )

    if (
        (
            "DBSnapshotIdentifier" not in self.properties
            and "SourceDBInstanceIdentifier" not in self.properties
        )
        and (
            "MasterUsername" not in self.properties
            or "MasterUserPassword" not in self.properties
        )
        and ("DBClusterIdentifier" not in self.properties)
    ):
        raise ValueError(
            r"Either (MasterUsername and MasterUserPassword) or"
            r" DBSnapshotIdentifier are required in type "
            r"AWS::RDS::DBInstance."
        )

    if "KmsKeyId" in self.properties and "StorageEncrypted" not in self.properties:
        raise ValueError(
            "If KmsKeyId is provided, StorageEncrypted is required "
            "AWS::RDS::DBInstance."
        )

    nonetype = type(None)
    avail_zone = self.properties.get("AvailabilityZone", None)
    multi_az = self.properties.get("MultiAZ", None)
    if not (
        isinstance(avail_zone, (AWSHelperFn, nonetype))
        and isinstance(multi_az, (AWSHelperFn, nonetype))
    ):
        if avail_zone and multi_az in [True, 1, "1", "true", "True"]:
            raise ValueError(
                "AvailabiltyZone cannot be set on "
                "DBInstance if MultiAZ is set to true."
            )

    storage_type = self.properties.get("StorageType", None)
    if storage_type and storage_type == "io1" and "Iops" not in self.properties:
        raise ValueError("Must specify Iops if using StorageType io1")

    allocated_storage = self.properties.get("AllocatedStorage")
    iops = self.properties.get("Iops", None)
    if iops and not isinstance(iops, AWSHelperFn):
        min_storage_size = 100
        engine = self.properties.get("Engine")
        if not isinstance(engine, AWSHelperFn) and engine.startswith("sqlserver"):
            min_storage_size = 20

        if (
            not isinstance(allocated_storage, AWSHelperFn)
            and int(allocated_storage) < min_storage_size
        ):
            raise ValueError(
                f"AllocatedStorage must be at least {min_storage_size} when "
                "Iops is set."
            )
        if (
            not isinstance(allocated_storage, AWSHelperFn)
            and not isinstance(iops, AWSHelperFn)
            and float(iops) / float(allocated_storage) > 50.0
        ):
            raise ValueError(
                "AllocatedStorage must be no less than " "1/50th the provisioned Iops"
            )


rds.DBInstance.validate = validate_dbinstance

# troposphere ec2 is quite old and SecurityGroupIngress is only a list
# without the obj type, this break auto_get_props, fix it overriding
ec2.SecurityGroup.props = {
    "GroupName": (str, False),
    "GroupDescription": (str, True),
    "SecurityGroupEgress": (list, False),
    "SecurityGroupIngress": ([ec2.SecurityGroupRule], False),
    "VpcId": (str, False),
    "Tags": ((Tags, list), False),
}
