import sys
import os

import unittest
# remember python3 supports mocks
from unittest.mock import patch
from copy import deepcopy
from time import sleep

import pytest

from tests.static.responses import (DESCRIBE_DB_INSTANCES, mocked_boto_rds_instance,
                                    mock_describe_db_clusters, mock_delete_db_cluster,
                                    mock_restore_db_cluster, mock_destroy_resource)
from tests.static.constants import CLUSTER_SUBNET, INSTANCE_SUBNET, DBInstanceIdentifier, DBClusterIdentifer

# begin local imports
from zenyatta.aws.rds import RDSResource, RDSDriver, SingleInstanceDriver, AuroraDriver, \
    create_rds_instance_from_point_in_time, delete_rds_resource, generate_etl_db_instance_identifier
from zenyatta.db.sql import PostgresSQL, MySQL

PARENT = '../'
SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))))
sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PARENT)))


class TestRDSResource:

    def test_identifier_from_host(self):
        host = "discovery-staging-master-pitr-etl-20161129t200000." \
            "cifgffw7w2ar.us-west-2.rds.amazonaws.com:5432"
        r = RDSResource(identifier=RDSResource.db_instance_identifier(host))
        assert r.identifier == "discovery-staging-master-pitr-etl-20161129t200000"

    def test_no_driver(self):
        r = RDSResource()
        assert type(r.driver) is RDSDriver
        # check basic not impleneted
        with pytest.raises(NotImplementedError):
            r.get_rds_metadata()
        # check the actual function doing some work in RDSResource
        with pytest.raises(NotImplementedError):
            r.does_rds_host_exist()


class TestSinglePostgresInstanceDriver(unittest.TestCase):

    def setUp(self):
        self.postgres_rds = RDSResource(
            identifier=DBInstanceIdentifier,
            driver=SingleInstanceDriver(sql_driver=PostgresSQL(DBInstanceIdentifier)))

    @patch('zenyatta.aws.rds.boto_client', return_value=mocked_boto_rds_instance())
    def test_subnet(self, boto_mock):
        assert self.postgres_rds.subnet() == INSTANCE_SUBNET

    @patch('zenyatta.aws.rds.boto_client', return_value=mocked_boto_rds_instance())
    def test_status(self, boto_mock):
        assert self.postgres_rds.status() == "available"

    @patch('zenyatta.aws.rds.boto_client', return_value=mocked_boto_rds_instance())
    def test_destroy(self, boto_mock):
        response = self.postgres_rds.destroy()
        assert response['DBInstance']['DBInstanceIdentifier'] == self.postgres_rds.identifier

    @patch('zenyatta.aws.rds.boto_client', return_value=mocked_boto_rds_instance())
    def test_point_in_time_recovery(self, boto_mock):
        from datetime import datetime
        target = deepcopy(self.postgres_rds)
        target.identifier += "-test"
        recovery, modify = self.postgres_rds.point_in_time_recovery(datetime.now(), target)
        assert recovery['DBInstance']['DBInstanceIdentifier'] == target.identifier
        assert recovery['DBInstance']['BackupRetentionPeriod'] is not 0
        assert modify['DBInstance']['BackupRetentionPeriod'] is not 0
        assert modify['DBInstance']['PendingModifiedValues']['BackupRetentionPeriod'] is 0
        assert modify['DBInstance']['PendingModifiedValues']['DBSecurityGroups'] == \
            target.security_groups()

    @patch('zenyatta.aws.rds.boto_client', return_value=mocked_boto_rds_instance())
    def test_host_and_port(self, boto_mock):
        host, port = self.postgres_rds.get_host_and_port()
        assert host is DESCRIBE_DB_INSTANCES['DBInstances'][0]['Endpoint']['Address']
        assert port is DESCRIBE_DB_INSTANCES['DBInstances'][0]['Endpoint']['Port']

    @patch('zenyatta.aws.rds.boto_client', return_value=mocked_boto_rds_instance())
    def test_get_rds_metadata(self, boto_mock):
        meta = self.postgres_rds.get_rds_metadata()
        assert meta is DESCRIBE_DB_INSTANCES['DBInstances'][0]


class TestAuroraDriver(unittest.TestCase):

    def setUp(self):
        self.aurora = RDSResource(identifier='just-a-test',
                                  driver=AuroraDriver(sql_driver=MySQL(DBClusterIdentifer)))

    @patch('zenyatta.aws.rds.sleep', return_value=sleep(0))
    @patch('zenyatta.aws.rds.boto_client', return_value=mock_describe_db_clusters())
    def test_subnet(self, boto_mock, slp_mock):
        assert self.aurora.subnet() == CLUSTER_SUBNET

    @patch('zenyatta.aws.rds.sleep', return_value=sleep(0))
    @patch('zenyatta.aws.rds.boto_client', return_value=mock_describe_db_clusters())
    def test_status(self, boto_mock, slp_mock):
        assert self.aurora.status() == "available"

    @patch('zenyatta.aws.rds.sleep', return_value=sleep(0))
    @patch('zenyatta.aws.rds.boto_client', return_value=mock_delete_db_cluster())
    def test_destroy(self, boto_mock, slp_mock):
        instance_response, cluster_response = self.aurora.destroy()
        assert instance_response['DBInstance']['DBClusterIdentifier'] == self.aurora.identifier
        assert instance_response['DBInstance']['DBInstanceIdentifier'] == self.aurora.reader_instance()
        assert cluster_response['DBCluster']['DBClusterIdentifier'] == self.aurora.identifier

    @patch('zenyatta.aws.rds.sleep', return_value=sleep(0))
    @patch('zenyatta.aws.rds.boto_client', return_value=mock_restore_db_cluster())
    def test_point_in_time_recovery(self, boto_mock, slp_mock):
        from datetime import datetime
        target = deepcopy(self.aurora)
        target.identifier += "-recovered"
        recovery, modify, create = self.aurora.point_in_time_recovery(datetime.now(), target)
        assert recovery['DBCluster']['DBClusterIdentifier'] == target.identifier
        assert modify['DBCluster']['DBClusterIdentifier'] == target.identifier
        assert create['DBInstance']['DBClusterIdentifier'] == target.identifier
        assert create['DBInstance']['DBInstanceIdentifier'] == target.reader_instance()

    @patch('zenyatta.aws.rds.sleep', return_value=sleep(0))
    @patch('zenyatta.aws.rds.boto_client', return_value=mock_describe_db_clusters())
    def test_host_and_port(self, boto_mock, slp_mock):
        host, port = self.aurora.get_host_and_port()
        assert int(port) == 3306
        assert host.startswith(self.aurora.identifier)

    @patch('zenyatta.aws.rds.sleep', return_value=sleep(0))
    @patch('zenyatta.aws.rds.boto_client', return_value=mock_describe_db_clusters())
    def test_get_rds_metadata(self, boto_mock, slp_mock):
        meta = self.aurora.get_rds_metadata()
        assert meta['DBClusterIdentifier'] == self.aurora.identifier


def test_create_rds_instance_from_point_in_time():
    # TODO come back to this
    source = RDSResource(identifier="postgres-test",
                         driver=SingleInstanceDriver(sql_driver=PostgresSQL('postgres-test')))
    """
    create_rds_instance_from_point_in_time(source,
                                           ts_nodash='19850125T0000',
                                           ts='1985-01-25T00:00:00')
    """


@patch('zenyatta.aws.rds.sleep', return_value=sleep(0))
@patch('zenyatta.aws.rds.boto_client', return_value=mock_destroy_resource())
def test_delete_rds_resource(boto, slp):
    source = RDSResource(identifier="postgres-test",
                         driver=SingleInstanceDriver(sql_driver=PostgresSQL('postgres-test')))
    assert delete_rds_resource(source) is True


def test_generate_etl_db_instance_identifier():
    prefix = 'test'
    suffix = '19850125T0000'
    identifier = generate_etl_db_instance_identifier(prefix, suffix)
    # output should be test-etl-19850125t0000
    assert identifier == prefix + '-etl-' + suffix.lower()
