import pytest
import pytz

from datetime import datetime
from psi_collector.custom_collector import CustomCollector
from test_utils.mock import MockApi

TEST_CAPTURE_TIME = datetime.fromisoformat('2021-06-01T00:00:00').replace(tzinfo=pytz.utc)


class TestCollector(CustomCollector):
    def __init__(self, aws_session = None):
        super().__init__('test', TEST_CAPTURE_TIME, None, aws_session, None)

    def _collect(self, s3_path: str = None):
        return [{'dummy': 'data'}, {'empty': ''}, {'number': 0}]

    def start_date(self, capture_time):
        return capture_time

    def end_date(self, capture_time):
        return capture_time
TestCollector.__test__ = False

class TestCustomCollector_UnitTests:

    @pytest.mark.unit
    def test_init_failure(self):
        # Assert exception raised when initializing (can't initialize an abstract class)
        with pytest.raises(Exception) as e_info:
            c = CustomCollector('test', TEST_CAPTURE_TIME, None, None, None)

    @pytest.mark.unit
    def test_collect(self):
        c = TestCollector()
        data = c.collect()
        assert len(data) == 3
        assert data[0] == {'dummy': 'data'}
        assert data[1] == {'empty': 'null'}
        assert data[2] == {'number': 0}

    @pytest.mark.unit
    def test_format_date(self):
        pacific = pytz.timezone("US/Pacific")
        date_with_tz = datetime(2021, 6, 1).replace(tzinfo=pytz.utc).astimezone(pacific)
        date_without_tz = datetime(2021, 6, 1)

        c = TestCollector()
        assert c.format_date(date_without_tz) == '2021-06-01T00:00:00Z'
        assert c.format_date(date_with_tz) == '2021-05-31T17:00:00-07:00'

    @pytest.mark.unit
    def test_parse_date(self):
        # Assert exception raised when bad date string is given
        c = TestCollector()
        with pytest.raises(Exception) as e_info:
            c.parse_date('bad string raise exception')

        # Parse a UTC date
        dt = c.parse_date('2021-06-01T09:08:07Z')
        assert dt.timestamp() == 1622538487.0
        assert dt.tzname() == 'UTC'

        # Parse a non-UTC date
        dt = c.parse_date('2021-06-01T09:08:07-05:00')
        assert dt.timestamp() == 1622556487.0
        assert dt.tzname() == 'UTC-05:00'

    @pytest.mark.unit
    def test_get_s3_object(self):
        # Set expected values
        expected_exception = Exception('Test exception')

        # Set up mock clients
        mock_s3_client = MockApi()
        mock_s3_client.on(
            'get_object', Bucket='bucket', Key='key'
        ).raise_exception(expected_exception)

        mock_session = MockApi()
        mock_session.on('client', 's3').return_val(mock_s3_client)

        # Test exception raised by S3 client
        c = TestCollector(aws_session=mock_session)
        with pytest.raises(Exception) as e_info:
            c._get_s3_object('s3://bucket/key')

    @pytest.mark.unit
    def test_get_parameter(self):
        # Set expected values
        expected_parameter_name = 'test'
        expected_parameter_value = 'parameter value'

        # Set up mock clients
        mock_ssm_client = MockApi()
        mock_ssm_client.on('get_parameter', Name=expected_parameter_name, WithDecryption=True).return_val({
            'Parameter': {'Value': expected_parameter_value}
        })

        mock_session = MockApi()
        mock_session.on('client', 'ssm').return_val(mock_ssm_client)

        c = TestCollector(aws_session=mock_session)
        value = c._get_parameter('test')
        assert value == expected_parameter_value

    @pytest.mark.unit
    def test_get_parameter_no_parameter_value(self):
        # Set expected values
        expected_parameter_name = 'test'
        expected_parameter_value = 'parameter value'

        # Set up mock clients
        mock_ssm_client = MockApi()
        mock_ssm_client.on(
            'get_parameter', Name=expected_parameter_name
        ).return_val({})

        mock_session = MockApi()
        mock_session.on('client', 'ssm').return_val(mock_ssm_client)

        # Test exception raised when no parameter value in client response
        c = TestCollector(aws_session=mock_session)
        with pytest.raises(Exception) as e_info:
            value = c._get_parameter('test')

    @pytest.mark.unit
    def test_repr(self):
        c = TestCollector()
        assert str(c) == '{"name": "test", "start_date": "2021-06-01T00:00:00Z", "end_date": "2021-06-01T00:00:00Z"}'
