import pytest
import pytz

from botocore.exceptions import ClientError
from datetime import datetime
from psi_utils.s3_loader import S3Loader
from psi_utils.unittest_mock import MockApi

TEST_CAPTURE_TIME = datetime.fromisoformat("2021-08-12T13:49:18.763").replace(tzinfo=pytz.utc)


class TestS3Loader_UnitTests:

    @pytest.mark.unit
    def test_load(self):
        # Set expected values
        expected_body = '{"key": "test value"}'
        expected_bucket = "test-bucket"
        expected_key = "scorecards-metrics/test_collector/2021/8/12/1628776158763"

        # Set up mock clients
        mock_client = MockApi()
        mock_client.on(
            "put_object",
            Body=expected_body,
            Bucket=expected_bucket,
            Key=expected_key,
            ACL="bucket-owner-full-control",
        ).return_val(None)
        mock_session = MockApi()
        mock_session.on("client", "s3").return_val(mock_client)

        # Initialize S3Loader and run load()
        loader = S3Loader(bucket=expected_bucket, aws_session=mock_session)
        s3_uri = loader.load(
            collector_name="test_collector",
            data={"key": "test value"},
            capture_time=TEST_CAPTURE_TIME,
        )
        assert s3_uri == f"s3://{expected_bucket}/{expected_key}"

        # Assert expectations
        mock_session.assert_expectations()
        mock_client.assert_expectations()

    @pytest.mark.unit
    def test_load_no_data(self):
        # Set up mock clients
        mock_session = MockApi()
        mock_session.on("client", "s3").return_val(MockApi())

        # Initialize S3Loader and run load()
        loader = S3Loader(bucket="test-bucket", aws_session=mock_session)
        s3_uri = loader.load(
            collector_name="test_collector",
            data={},
            capture_time=TEST_CAPTURE_TIME,
        )
        assert s3_uri == None

        # Assert expectations
        mock_session.assert_expectations()

    @pytest.mark.unit
    def test_put_object(self):
        # Set expected values
        expected_body = '{"key": "test value"}'
        expected_bucket = "test-bucket"
        expected_key = "test-key"
        expected_exception = ClientError({}, "put_object")

        # Set up mock clients
        mock_client = MockApi()
        mock_client.on(
            "put_object",
            Body=expected_body,
            Bucket=expected_bucket,
            Key=expected_key,
            ACL="bucket-owner-full-control",
        ).raise_exception(expected_exception)
        mock_session = MockApi()
        mock_session.on("client", "s3").return_val(mock_client)

        # Assert exception raised when loader.__put_object is called
        loader = S3Loader(bucket=expected_bucket, aws_session=mock_session)
        with pytest.raises(Exception) as e_info:
            loader._S3Loader__put_object(expected_key, expected_body, expected_bucket)
        assert e_info.value == expected_exception

        # Assert expectations
        mock_session.assert_expectations()
        mock_client.assert_expectations()

    @pytest.mark.unit
    def test_dt_to_ms(self):
        # Set up mock clients
        mock_session = MockApi()
        mock_session.on("client", "s3").return_val(MockApi())

        loader = S3Loader(bucket="test-bucket", aws_session=mock_session)
        assert loader._S3Loader__dt_to_ms(TEST_CAPTURE_TIME) == 1628776158763
        assert loader._S3Loader__dt_to_ms(None) == -1
