import logging
import random

import pytest

from ocs_ci.framework.pytest_customization.marks import aws_platform_required
from ocs_ci.framework.testlib import ManageTest, tier4, tier4b, bugzilla
from ocs_ci.ocs.exceptions import CommandFailed
from tests import sanity_helpers

logger = logging.getLogger(__name__)


@tier4
@tier4b
@pytest.mark.polarion_id("OCS-1287")
@aws_platform_required
@bugzilla('1754287')
class TestAvailabilityZones(ManageTest):
    """
    test availability zone failure:
    test stages:
    1. Select availability zone
    2. In this availability zone, backup instances original security groups
    3. block availability zone by attaching security group with no permissions
    4. validate - cluster functionality and health
        2a. health check - warning or error
        2b. create cephfs, create rbd, create pvc (validate_cluster)

    5. restore availability zone access
    6. validate - cluster functionality and health
    """
    @pytest.fixture(autouse=True)
    def init_sanity(self):
        """
        init Sanity() object
        """

        self.sanity_helpers = sanity_helpers.Sanity()

    @pytest.fixture()
    def teardown(self, request, ec2_instances, aws_obj):

        def finalizer():
            current_sg = aws_obj.store_security_groups_for_instances(self.instances_in_az)
            if self.original_sgs != current_sg:
                aws_obj.restore_instances_access(self.security_group_id, self.original_sgs)
                logger.info(f"Access to EC2 instances {self.instances_in_az} has been restored")

            if self.security_group_id in aws_obj.get_all_security_groups():
                logger.info(f"Deleting: {self.security_group_id}")
                aws_obj.delete_security_group(self.security_group_id)

        request.addfinalizer(finalizer)

    def test_availability_zone_failure(
        self, aws_obj, ec2_instances, pvc_factory, pod_factory, teardown
    ):
        """

        Simulate AWS availability zone failure

        """

        # Select instances in randomly chosen availability zone:
        self.instances_in_az = self.random_availability_zone_selector(aws_obj, ec2_instances)
        logger.info(f"AZ selected, Instances: {self.instances_in_az} to be blocked")

        # Storing current security groups for selected instances:
        self.original_sgs = aws_obj.store_security_groups_for_instances(self.instances_in_az)
        logger.info(f"Original security groups of selected instances: {self.original_sgs}")

        # Blocking instances:
        self.security_group_id = self.block_aws_availability_zone(aws_obj, self.instances_in_az)
        logger.info(f"Access to EC2 instances {self.instances_in_az} has been blocked")

        # Check cluster's health, need to be unhealthy at that point

        assert not self.check_cluster_health(), (
            "Cluster is wrongly reported as healthy."
            "EC2 Instances {self.instances_in_az} are blocked"
        )

        # Create resources
        logger.info("Trying to create resources on un-healthy cluster")
        self.sanity_helpers.create_resources(pvc_factory, pod_factory)
        logger.info("Resources Created")

        # Delete resources
        logger.info("Trying to delete resources on un-healthy cluster")
        self.sanity_helpers.delete_resources()
        logger.info("Resources Deleted")

        # Restore access for blocked instances
        aws_obj.restore_instances_access(self.security_group_id, self.original_sgs)
        logger.info(f"Access restores")

        # Check cluster's health, need to be healthy at that point

        assert self.check_cluster_health(), "Cluster is unhealthy"

    def random_availability_zone_selector(self, aws_obj, ec2_instances):
        """
        Get all instances within random availability zone

        Args:
            aws_obj (obj): aws.AWS() object
            ec2_instances (dict): cluster ec2 instances objects

        Returns:
            list: instances_in_az

        """
        random_az_selector = random.choice(list(ec2_instances.keys()))
        random_az_selected = aws_obj.get_availability_zone_id_by_instance_id(random_az_selector)
        instances_in_az = list()
        for instance in ec2_instances.keys():
            az = aws_obj.get_availability_zone_id_by_instance_id(instance)
            if random_az_selected == az:
                instances_in_az.append(instance)

        return instances_in_az

    def block_aws_availability_zone(self, aws_obj, instances_in_az):
        """
        1. get vpc_id
        2. create security group in this vpc
        3. block availability zone by using "append_security_group"

        Args:
            aws_obj (obj): aws.AWS() object
            instances_in_az (list): ec2_instances within selected availability zone

        Returns:
            security_group_id (str): Newly created security id without access permissions

        """
        group_name = "TEST_SEC_GROUP"
        dict_permissions = {'IpProtocol': 'tcp',
                            'FromPort': 80,
                            'ToPort': 80,
                            'IpRanges': [{'CidrIp': '1.1.1.1/32'}]}
        vpc_id = aws_obj.get_vpc_id_by_instance_id(instances_in_az[0])
        security_group_id = aws_obj.create_security_group(group_name, dict_permissions, vpc_id)
        aws_obj.block_instances_access(security_group_id, instances_in_az)

        return security_group_id

    def check_cluster_health(self):
        try:
            self.sanity_helpers.health_check()
            return True
        except CommandFailed as e:
            if "Unable to connect to the server" in str(e):
                logger.warning(f"{e}, Cluster is not healthy")
                return False
