from unittest.mock import patch

import pytest
from src.error.exceptions import FileUploadException
from src.system_var.constants import END_JP_NAME
from src.upload_result_data_process import upload_result_data_process
from src.util.execute_datetime import ExecuteDateTime

from .test_utils.log_message import generate_log_message_tuple


class TestUploadResultDataProcess:

    @pytest.fixture
    def bucket_name(self):
        return 'test-config-bucket'

    @pytest.fixture
    def prepare_bucket(self, s3_client, bucket_name):
        s3_client.create_bucket(Bucket=bucket_name)
        yield

    def test_run_process_success(self, s3_client, prepare_bucket, bucket_name, monkeypatch, caplog):
        """
        Cases:
            取得処理実施結果アップロード処理が正常終了し、期待通りの結果が返ること
        Arranges:
            - prepare_bucketフィクスチャで、取得処理実施結果ファイルを置くためのモックバケットを作る
            - モックバケットを指すように環境変数を設定する
            - 取得処理実施結果を準備する
            - 実行日時取得インスタンスを生成する
        Expects:
            - PUTした取得処理実施結果ファイルが存在し内容が想定と一致する
            - 取得処理実施結果アップロード処理の仕様に沿った正常系ログが出力されること(デバッグログは除く)
        """

        # Arranges

        # 環境変数を編集
        monkeypatch.setattr('src.aws.s3.CRM_BACKUP_BUCKET', bucket_name)
        monkeypatch.setattr('src.aws.s3.PROCESS_RESULT_FOLDER', 'data_import')

        process_result = {
            "Account": "success",
            "Contact": "fail"
        }

        execute_datetime = ExecuteDateTime()

        # Act
        upload_result_data_process(process_result, execute_datetime)

        # Assert
        # ファイル確認
        actual = s3_client.get_object(Bucket=bucket_name, Key=f'data_import/{execute_datetime.to_path()}/process_result.json')
        assert actual['Body'].read().decode('utf-8') == '{"Account": "success", "Contact": "fail"}'

        # ログの確認
        assert generate_log_message_tuple(log_message='I-END-01 取得処理実施結果アップロード処理を開始します') in caplog.record_tuples
        assert generate_log_message_tuple(log_message=f'I-END-03 取得処理実施結果アップロード処理を終了します') in caplog.record_tuples

    def test_call_depended_modules(self):
        """
        Cases:
            取得処理実施結果アップロード処理内で依存しているモジュールが正しく呼ばれていること
        Arranges:
            - 取得処理実施結果アップロード処理の依存モジュールをモック化する
            - 取得処理実施結果を準備する
            - 実行日時取得インスタンスを生成する
        Expects:
            - 依存しているモジュールが正しく呼ばれている
        """

        # Arrange

        process_result = {
            "Account": "success",
            "Contact": "fail"
        }

        execute_datetime = ExecuteDateTime()

        # Act
        with patch('src.upload_result_data_process.BackupBucket') as mock_backup_bucket, \
                patch('src.upload_result_data_process.JsonWriter') as mock_writer:
            mock_writer_inst = mock_writer.return_value
            mock_writer_inst.write.return_value = ''
            mock_backup_bucket_inst = mock_backup_bucket.return_value
            mock_backup_bucket_inst.put_result_json.return_value = ''

            upload_result_data_process(process_result, execute_datetime)

        # Assert
        assert mock_writer_inst.write.called is True
        assert mock_backup_bucket_inst.put_result_json.called is True

    def test_raise_put_result_json(self, monkeypatch):
        """
        Cases:
            取得処理実施結果をアップロードできない場合、エラーが発生すること
        Arranges:
            - 取得処理実施結果アップロード処理で例外を発生させるモックを生成する
            - 取得処理実施結果を準備する
            - 実行日時取得インスタンスを生成する
        Expects:
            - 例外が発生する
            - ファイルがアップロードできないエラーが返却される
        """

        # Arrange

        process_result = {
            "Account": "success",
            "Contact": "fail"
        }

        execute_datetime = ExecuteDateTime()

        # Act
        with patch('src.upload_result_data_process.BackupBucket') as mock_backup_bucket, \
                patch('src.upload_result_data_process.JsonWriter') as mock_writer:
            mock_writer_inst = mock_writer.return_value
            mock_writer_inst.write.return_value = ''
            mock_backup_bucket_inst = mock_backup_bucket.return_value
            mock_backup_bucket_inst.put_result_json.side_effect = Exception('ファイルアップロードエラー')

            with pytest.raises(FileUploadException) as e:
                upload_result_data_process(process_result, execute_datetime)

        # Assert
        assert mock_writer_inst.write.called is True
        assert mock_backup_bucket.called is True
        assert e.value.error_id == 'E-END-01'
        assert e.value.func_name == END_JP_NAME
        assert e.value.args[0] == f'取得処理実施結果のアップロードに失敗しました　ファイル名：[process_result.json] エラー内容：[ファイルアップロードエラー]'

    def test_raise_put_result_json_write_local_file(self, monkeypatch):
        """
        Cases:
            取得処理実施結果をアップロードできない場合、エラーが発生すること
        Arranges:
            - 取得処理実施結果アップロード処理で例外を発生させるモックを生成する
            - 取得処理実施結果を準備する
            - 実行日時取得インスタンスを生成する
        Expects:
            - 例外が発生する
            - ファイルがアップロードできないエラーが返却される
        """

        # Arrange

        process_result = {
            "Account": "success",
            "Contact": "fail"
        }

        execute_datetime = ExecuteDateTime()

        # Act
        with patch('src.upload_result_data_process.BackupBucket') as mock_backup_bucket, \
                patch('src.upload_result_data_process.JsonWriter') as mock_writer:
            mock_writer_inst = mock_writer.return_value
            mock_writer_inst.write.side_effect = Exception('ファイル書き込みエラー')
            mock_backup_bucket_inst = mock_backup_bucket.return_value
            mock_backup_bucket_inst.put_result_json.return_value = ''

            with pytest.raises(FileUploadException) as e:
                upload_result_data_process(process_result, execute_datetime)

        # Assert
        assert mock_writer_inst.write.called is True
        assert mock_backup_bucket.called is False
        assert e.value.error_id == 'E-END-01'
        assert e.value.func_name == END_JP_NAME
        assert e.value.args[0] == f'取得処理実施結果のアップロードに失敗しました　ファイル名：[process_result.json] エラー内容：[ファイル書き込みエラー]'
