import json
from mock import patch, MagicMock
from . import base
from buildercore.core import parse_stackname
from buildercore import cfngen, context_handler, lifecycle


class TestBuildercoreLifecycle(base.BaseCase):
    @classmethod
    def setUpClass(cls):
        cls._generate_context('dummy1--test')
        cls._generate_context('project-with-rds-only--test')

    @patch('buildercore.lifecycle._some_node_is_not_ready')
    @patch('buildercore.lifecycle._ec2_connection')
    @patch('buildercore.lifecycle.find_rds_instances')
    @patch('buildercore.lifecycle.find_ec2_instances')
    def test_start_a_not_running_ec2_instance(self, find_ec2_instances, find_rds_instances, ec2_connection, some_node_is_not_ready):
        find_ec2_instances.side_effect = [
            [self._ec2_instance('stopped')],
            [self._ec2_instance('running')],
            [self._ec2_instance('running')] # update_dns additional call
        ]
        some_node_is_not_ready.return_value = False

        c = MagicMock()
        c.start_instances = MagicMock()
        ec2_connection.return_value = c
        lifecycle.start('dummy1--test')

    @patch('buildercore.lifecycle._rds_connection')
    @patch('buildercore.lifecycle.find_rds_instances')
    @patch('buildercore.lifecycle.find_ec2_instances')
    def test_start_a_not_running_rds_instance(self, find_ec2_instances, find_rds_instances, rds_connection):
        find_ec2_instances.return_value = []
        find_rds_instances.side_effect = [
            [self._rds_instance('stopped')],
            [self._rds_instance('available')]
        ]

        c = MagicMock()
        c.start_db_instance = MagicMock()
        rds_connection.return_value = c

        lifecycle.start('project-with-rds-only--test')

    @patch('buildercore.lifecycle.find_rds_instances')
    @patch('buildercore.lifecycle.find_ec2_instances')
    def test_start_ec2_idempotence(self, find_ec2_instances, find_rds_instances):
        find_ec2_instances.return_value = [self._ec2_instance('running')]
        lifecycle.start('dummy1--test')

    @patch('buildercore.lifecycle.find_rds_instances')
    @patch('buildercore.lifecycle.find_ec2_instances')
    def test_start_rds_idempotence(self, find_ec2_instances, find_rds_instances):
        find_rds_instances.return_value = [self._rds_instance('available')]
        lifecycle.start('project-with-rds-only--test')

    @patch('buildercore.lifecycle.find_ec2_instances')
    def test_ignores_terminated_stacks_that_have_been_replaced_by_a_new_instance(self, find_ec2_instances):
        old = self._ec2_instance('terminated', 'i-123')
        new = self._ec2_instance('running', 'i-456')

        find_ec2_instances.return_value = [old, new]
        self.assertEqual({'i-456': 'running'}, lifecycle._ec2_nodes_states('dummy1--test'))

    @patch('buildercore.lifecycle._stop')
    @patch('buildercore.lifecycle.find_rds_instances')
    @patch('buildercore.lifecycle.find_ec2_instances')
    def test_stops_ec2_instance(self, find_ec2_instances, find_rds_instances, _stop):
        find_ec2_instances.return_value = [self._ec2_instance('running')]
        find_rds_instances.return_value = []
        lifecycle.stop('dummy1--test')

    @patch('buildercore.lifecycle._stop')
    @patch('buildercore.lifecycle.find_rds_instances')
    @patch('buildercore.lifecycle.find_ec2_instances')
    def test_stops_rds_instance(self, find_ec2_instances, find_rds_instances, _stop):
        find_ec2_instances.return_value = []
        find_rds_instances.return_value = [self._rds_instance('available')]
        lifecycle.stop('project-with-rds-only--test')

    @patch('buildercore.lifecycle._stop')
    @patch('buildercore.lifecycle.find_ec2_instances')
    def test_stops_instances_when_running_for_too_many_minutes(self, find_ec2_instances, _stop):

        find_ec2_instances.return_value = [self._ec2_instance('running', launch_time='2000-01-01T00:00:00.000Z')]
        lifecycle.stop_if_running_for('dummy1--test', 30)

    @staticmethod
    def _generate_context(stackname):
        (pname, instance_id) = parse_stackname(stackname)
        context = cfngen.build_context(pname, stackname=stackname)
        context_handler.write_context_locally(stackname, json.dumps(context))

    def _ec2_instance(self, state='running', id='i-456', launch_time='2017-01-01T00:00:00.000Z'):
        instance = MagicMock()
        instance.id = id
        state_codes = {'running': 16}
        instance.state = {'Code': state_codes.get(state, -1), 'Name': state} # 'Code' should vary but probably isn't being used
        instance.tags = [{'Key': 'Name', 'Value': 'dummy1--test--1'}]
        instance.launch_time = launch_time
        return instance

    def _rds_instance(self, state='available', id='i-456'):
        instance = {
            'DBInstanceIdentifier': id,
            'DBInstanceStatus': state,
        }
        return instance

@@ -5,16 +5,46 @@
 rt json%0A
+from datetime import datetime%0A
 from moc
@@ -69,16 +69,37 @@
 gicMock%0A
+from pytz import utc%0A
 from . i
@@ -270,43 +270,22 @@
     
-@classmethod%0A    def setUpClass(cls
+def setUp(self
 ):%0A 
@@ -283,35 +283,36 @@
 (self):%0A        
-cls
+self
 ._generate_conte
@@ -342,11 +342,12 @@
     
-cls
+self
 ._ge
@@ -3931,34 +3931,40 @@
 ime=
-'2000-01-01T00:00:00.000Z'
+datetime(2000, 1, 1, tzinfo=utc)
 )%5D%0A 
@@ -4025,26 +4025,8 @@
 0)%0A%0A
-    @staticmethod%0A
     
@@ -4047,16 +4047,22 @@
 context(
+self, 
 stacknam
@@ -4342,34 +4342,40 @@
 ime=
-'2017-01-01T00:00:00.000Z'
+datetime(2017, 1, 1, tzinfo=utc)
 ):%0A 

Update lifecycle tests to new Boto behavior on `launch_time` field