"""Tests for storage abstraction layer."""

import io
from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from botocore.exceptions import ClientError

from reflector.storage.base import StoragePermissionError
from reflector.storage.storage_aws import AwsStorage


@pytest.mark.asyncio
async def test_aws_storage_stream_to_fileobj():
    """Test that AWS storage can stream directly to a file object without loading into memory."""
    # Setup
    storage = AwsStorage(
        aws_bucket_name="test-bucket",
        aws_region="us-east-1",
        aws_access_key_id="test-key",
        aws_secret_access_key="test-secret",
    )

    # Mock download_fileobj to write data
    async def mock_download(Bucket, Key, Fileobj, **kwargs):
        Fileobj.write(b"chunk1chunk2")

    mock_client = AsyncMock()
    mock_client.download_fileobj = AsyncMock(side_effect=mock_download)
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)

    # Patch the session client
    with patch.object(storage.session, "client", return_value=mock_client):
        # Create a file-like object to stream to
        output = io.BytesIO()

        # Act - stream to file object
        await storage.stream_to_fileobj("test-file.mp4", output, bucket="test-bucket")

        # Assert
        mock_client.download_fileobj.assert_called_once_with(
            Bucket="test-bucket", Key="test-file.mp4", Fileobj=output
        )

        # Check that data was written to output
        output.seek(0)
        assert output.read() == b"chunk1chunk2"


@pytest.mark.asyncio
async def test_aws_storage_stream_to_fileobj_with_folder():
    """Test streaming with folder prefix in bucket name."""
    storage = AwsStorage(
        aws_bucket_name="test-bucket/recordings",
        aws_region="us-east-1",
        aws_access_key_id="test-key",
        aws_secret_access_key="test-secret",
    )

    async def mock_download(Bucket, Key, Fileobj, **kwargs):
        Fileobj.write(b"data")

    mock_client = AsyncMock()
    mock_client.download_fileobj = AsyncMock(side_effect=mock_download)
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)

    with patch.object(storage.session, "client", return_value=mock_client):
        output = io.BytesIO()
        await storage.stream_to_fileobj("file.mp4", output, bucket="other-bucket")

        # Should use folder prefix from instance config
        mock_client.download_fileobj.assert_called_once_with(
            Bucket="other-bucket", Key="recordings/file.mp4", Fileobj=output
        )


@pytest.mark.asyncio
async def test_storage_base_class_stream_to_fileobj():
    """Test that base Storage class has stream_to_fileobj method."""
    from reflector.storage.base import Storage

    # Verify method exists in base class
    assert hasattr(Storage, "stream_to_fileobj")

    # Create a mock storage instance
    storage = MagicMock(spec=Storage)
    storage.stream_to_fileobj = AsyncMock()

    # Should be callable
    await storage.stream_to_fileobj("file.mp4", io.BytesIO())
    storage.stream_to_fileobj.assert_called_once()


@pytest.mark.asyncio
async def test_aws_storage_stream_uses_download_fileobj():
    """Test that download_fileobj is called correctly."""
    storage = AwsStorage(
        aws_bucket_name="test-bucket",
        aws_region="us-east-1",
        aws_access_key_id="test-key",
        aws_secret_access_key="test-secret",
    )

    async def mock_download(Bucket, Key, Fileobj, **kwargs):
        Fileobj.write(b"data")

    mock_client = AsyncMock()
    mock_client.download_fileobj = AsyncMock(side_effect=mock_download)
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)

    with patch.object(storage.session, "client", return_value=mock_client):
        output = io.BytesIO()
        await storage.stream_to_fileobj("test.mp4", output)

        # Verify download_fileobj was called with correct parameters
        mock_client.download_fileobj.assert_called_once_with(
            Bucket="test-bucket", Key="test.mp4", Fileobj=output
        )


@pytest.mark.asyncio
async def test_aws_storage_handles_access_denied_error():
    """Test that AccessDenied errors are caught and wrapped in StoragePermissionError."""
    storage = AwsStorage(
        aws_bucket_name="test-bucket",
        aws_region="us-east-1",
        aws_access_key_id="test-key",
        aws_secret_access_key="test-secret",
    )

    # Mock ClientError with AccessDenied
    error_response = {"Error": {"Code": "AccessDenied", "Message": "Access Denied"}}
    mock_client = AsyncMock()
    mock_client.put_object = AsyncMock(
        side_effect=ClientError(error_response, "PutObject")
    )
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)

    with patch.object(storage.session, "client", return_value=mock_client):
        with pytest.raises(StoragePermissionError) as exc_info:
            await storage.put_file("test.txt", b"data")

        # Verify error message contains expected information
        error_msg = str(exc_info.value)
        assert "AccessDenied" in error_msg
        assert "default bucket 'test-bucket'" in error_msg
        assert "S3 upload failed" in error_msg


@pytest.mark.asyncio
async def test_aws_storage_handles_no_such_bucket_error():
    """Test that NoSuchBucket errors are caught and wrapped in StoragePermissionError."""
    storage = AwsStorage(
        aws_bucket_name="test-bucket",
        aws_region="us-east-1",
        aws_access_key_id="test-key",
        aws_secret_access_key="test-secret",
    )

    # Mock ClientError with NoSuchBucket
    error_response = {
        "Error": {
            "Code": "NoSuchBucket",
            "Message": "The specified bucket does not exist",
        }
    }
    mock_client = AsyncMock()
    mock_client.delete_object = AsyncMock(
        side_effect=ClientError(error_response, "DeleteObject")
    )
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)

    with patch.object(storage.session, "client", return_value=mock_client):
        with pytest.raises(StoragePermissionError) as exc_info:
            await storage.delete_file("test.txt")

        # Verify error message contains expected information
        error_msg = str(exc_info.value)
        assert "NoSuchBucket" in error_msg
        assert "default bucket 'test-bucket'" in error_msg
        assert "S3 delete failed" in error_msg


@pytest.mark.asyncio
async def test_aws_storage_error_message_with_bucket_override():
    """Test that error messages correctly show overridden bucket."""
    storage = AwsStorage(
        aws_bucket_name="default-bucket",
        aws_region="us-east-1",
        aws_access_key_id="test-key",
        aws_secret_access_key="test-secret",
    )

    # Mock ClientError with AccessDenied
    error_response = {"Error": {"Code": "AccessDenied", "Message": "Access Denied"}}
    mock_client = AsyncMock()
    mock_client.get_object = AsyncMock(
        side_effect=ClientError(error_response, "GetObject")
    )
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)

    with patch.object(storage.session, "client", return_value=mock_client):
        with pytest.raises(StoragePermissionError) as exc_info:
            await storage.get_file("test.txt", bucket="override-bucket")

        # Verify error message shows overridden bucket, not default
        error_msg = str(exc_info.value)
        assert "overridden bucket 'override-bucket'" in error_msg
        assert "default-bucket" not in error_msg
        assert "S3 download failed" in error_msg


@pytest.mark.asyncio
async def test_aws_storage_reraises_non_handled_errors():
    """Test that non-AccessDenied/NoSuchBucket errors are re-raised as-is."""
    storage = AwsStorage(
        aws_bucket_name="test-bucket",
        aws_region="us-east-1",
        aws_access_key_id="test-key",
        aws_secret_access_key="test-secret",
    )

    # Mock ClientError with different error code
    error_response = {
        "Error": {"Code": "InternalError", "Message": "Internal Server Error"}
    }
    mock_client = AsyncMock()
    mock_client.put_object = AsyncMock(
        side_effect=ClientError(error_response, "PutObject")
    )
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)

    with patch.object(storage.session, "client", return_value=mock_client):
        # Should raise ClientError, not StoragePermissionError
        with pytest.raises(ClientError) as exc_info:
            await storage.put_file("test.txt", b"data")

        # Verify it's the original ClientError
        assert exc_info.value.response["Error"]["Code"] == "InternalError"


@pytest.mark.asyncio
async def test_aws_storage_presign_url_handles_errors():
    """Test that presigned URL generation handles permission errors."""
    storage = AwsStorage(
        aws_bucket_name="test-bucket",
        aws_region="us-east-1",
        aws_access_key_id="test-key",
        aws_secret_access_key="test-secret",
    )

    # Mock ClientError with AccessDenied during presign operation
    error_response = {"Error": {"Code": "AccessDenied", "Message": "Access Denied"}}
    mock_client = AsyncMock()
    mock_client.generate_presigned_url = AsyncMock(
        side_effect=ClientError(error_response, "GeneratePresignedUrl")
    )
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)

    with patch.object(storage.session, "client", return_value=mock_client):
        with pytest.raises(StoragePermissionError) as exc_info:
            await storage.get_file_url("test.txt")

        # Verify error message
        error_msg = str(exc_info.value)
        assert "S3 presign failed" in error_msg
        assert "AccessDenied" in error_msg


@pytest.mark.asyncio
async def test_aws_storage_list_objects_handles_errors():
    """Test that list_objects handles permission errors."""
    storage = AwsStorage(
        aws_bucket_name="test-bucket",
        aws_region="us-east-1",
        aws_access_key_id="test-key",
        aws_secret_access_key="test-secret",
    )

    # Mock ClientError during list operation
    error_response = {"Error": {"Code": "AccessDenied", "Message": "Access Denied"}}
    mock_paginator = MagicMock()

    async def mock_paginate(*args, **kwargs):
        raise ClientError(error_response, "ListObjectsV2")
        yield  # Make it an async generator

    mock_paginator.paginate = mock_paginate

    mock_client = AsyncMock()
    mock_client.get_paginator = MagicMock(return_value=mock_paginator)
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)

    with patch.object(storage.session, "client", return_value=mock_client):
        with pytest.raises(StoragePermissionError) as exc_info:
            await storage.list_objects(prefix="test/")

        error_msg = str(exc_info.value)
        assert "S3 list_objects failed" in error_msg
        assert "AccessDenied" in error_msg


def test_aws_storage_constructor_rejects_mixed_auth():
    """Test that constructor rejects both role_arn and access keys."""
    with pytest.raises(ValueError, match="cannot use both.*role_arn.*access keys"):
        AwsStorage(
            aws_bucket_name="test-bucket",
            aws_region="us-east-1",
            aws_access_key_id="test-key",
            aws_secret_access_key="test-secret",
            aws_role_arn="arn:aws:iam::123456789012:role/test-role",
        )


@pytest.mark.asyncio
async def test_aws_storage_custom_endpoint_url():
    """Test that custom endpoint_url configures path-style addressing and passes endpoint to client."""
    storage = AwsStorage(
        aws_bucket_name="reflector-media",
        aws_region="garage",
        aws_access_key_id="GKtest",
        aws_secret_access_key="secret",
        aws_endpoint_url="http://garage:3900",
    )
    assert storage._endpoint_url == "http://garage:3900"
    assert storage.boto_config.s3["addressing_style"] == "path"
    assert storage.base_url == "http://garage:3900/reflector-media/"
    # retries config preserved (merge, not replace)
    assert storage.boto_config.retries["max_attempts"] == 3

    mock_client = AsyncMock()
    mock_client.put_object = AsyncMock()
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=None)
    mock_client.generate_presigned_url = AsyncMock(
        return_value="http://garage:3900/reflector-media/test.txt"
    )

    with patch.object(
        storage.session, "client", return_value=mock_client
    ) as mock_session_client:
        await storage.put_file("test.txt", b"data")
        mock_session_client.assert_called_with(
            "s3", config=storage.boto_config, endpoint_url="http://garage:3900"
        )


@pytest.mark.asyncio
async def test_aws_storage_none_endpoint_url():
    """Test that None endpoint preserves current AWS behavior."""
    storage = AwsStorage(
        aws_bucket_name="reflector-bucket",
        aws_region="us-east-1",
        aws_access_key_id="AKIAtest",
        aws_secret_access_key="secret",
    )
    assert storage._endpoint_url is None
    assert storage.base_url == "https://reflector-bucket.s3.amazonaws.com/"
    # No s3 addressing_style override — boto_config should only have retries
    assert not hasattr(storage.boto_config, "s3") or storage.boto_config.s3 is None


# --- Tests for get_source_storage() ---


def test_get_source_storage_daily_with_credentials():
    """Daily platform with access keys returns AwsStorage with Daily credentials."""
    with patch("reflector.storage.settings") as mock_settings:
        mock_settings.DAILYCO_STORAGE_AWS_ACCESS_KEY_ID = "daily-key"
        mock_settings.DAILYCO_STORAGE_AWS_SECRET_ACCESS_KEY = "daily-secret"
        mock_settings.DAILYCO_STORAGE_AWS_BUCKET_NAME = "daily-bucket"
        mock_settings.DAILYCO_STORAGE_AWS_REGION = "us-west-2"

        from reflector.storage import get_source_storage

        storage = get_source_storage("daily")

        assert isinstance(storage, AwsStorage)
        assert storage._bucket_name == "daily-bucket"
        assert storage._region == "us-west-2"
        assert storage._access_key_id == "daily-key"
        assert storage._secret_access_key == "daily-secret"
        assert storage._endpoint_url is None


def test_get_source_storage_daily_falls_back_without_credentials():
    """Daily platform without access keys falls back to transcript storage."""
    with patch("reflector.storage.settings") as mock_settings:
        mock_settings.DAILYCO_STORAGE_AWS_ACCESS_KEY_ID = None
        mock_settings.DAILYCO_STORAGE_AWS_SECRET_ACCESS_KEY = None
        mock_settings.DAILYCO_STORAGE_AWS_BUCKET_NAME = "daily-bucket"
        mock_settings.TRANSCRIPT_STORAGE_BACKEND = "aws"
        mock_settings.TRANSCRIPT_STORAGE_AWS_BUCKET_NAME = "transcript-bucket"
        mock_settings.TRANSCRIPT_STORAGE_AWS_REGION = "us-east-1"
        mock_settings.TRANSCRIPT_STORAGE_AWS_ACCESS_KEY_ID = "transcript-key"
        mock_settings.TRANSCRIPT_STORAGE_AWS_SECRET_ACCESS_KEY = "transcript-secret"
        mock_settings.TRANSCRIPT_STORAGE_AWS_ENDPOINT_URL = None

        from reflector.storage import get_source_storage

        with patch("reflector.storage.get_transcripts_storage") as mock_get_transcripts:
            fallback = AwsStorage(
                aws_bucket_name="transcript-bucket",
                aws_region="us-east-1",
                aws_access_key_id="transcript-key",
                aws_secret_access_key="transcript-secret",
            )
            mock_get_transcripts.return_value = fallback

            storage = get_source_storage("daily")

            mock_get_transcripts.assert_called_once()
            assert storage is fallback


def test_get_source_storage_whereby_with_credentials():
    """Whereby platform with access keys returns AwsStorage with Whereby credentials."""
    with patch("reflector.storage.settings") as mock_settings:
        mock_settings.WHEREBY_STORAGE_AWS_ACCESS_KEY_ID = "whereby-key"
        mock_settings.WHEREBY_STORAGE_AWS_SECRET_ACCESS_KEY = "whereby-secret"
        mock_settings.WHEREBY_STORAGE_AWS_BUCKET_NAME = "whereby-bucket"
        mock_settings.WHEREBY_STORAGE_AWS_REGION = "eu-west-1"

        from reflector.storage import get_source_storage

        storage = get_source_storage("whereby")

        assert isinstance(storage, AwsStorage)
        assert storage._bucket_name == "whereby-bucket"
        assert storage._region == "eu-west-1"
        assert storage._access_key_id == "whereby-key"
        assert storage._secret_access_key == "whereby-secret"


def test_get_source_storage_unknown_platform_falls_back():
    """Unknown platform falls back to transcript storage."""
    with patch("reflector.storage.settings"):
        from reflector.storage import get_source_storage

        with patch("reflector.storage.get_transcripts_storage") as mock_get_transcripts:
            fallback = MagicMock()
            mock_get_transcripts.return_value = fallback

            storage = get_source_storage("unknown-platform")

            mock_get_transcripts.assert_called_once()
            assert storage is fallback


@pytest.mark.asyncio
async def test_source_storage_presigns_for_correct_bucket():
    """Source storage presigns URLs using the platform's credentials and the override bucket."""
    with patch("reflector.storage.settings") as mock_settings:
        mock_settings.DAILYCO_STORAGE_AWS_ACCESS_KEY_ID = "daily-key"
        mock_settings.DAILYCO_STORAGE_AWS_SECRET_ACCESS_KEY = "daily-secret"
        mock_settings.DAILYCO_STORAGE_AWS_BUCKET_NAME = "daily-bucket"
        mock_settings.DAILYCO_STORAGE_AWS_REGION = "us-west-2"

        from reflector.storage import get_source_storage

        storage = get_source_storage("daily")

        mock_client = AsyncMock()
        mock_client.generate_presigned_url = AsyncMock(
            return_value="https://daily-bucket.s3.amazonaws.com/track.webm?signed"
        )
        mock_client.__aenter__ = AsyncMock(return_value=mock_client)
        mock_client.__aexit__ = AsyncMock(return_value=None)

        with patch.object(storage.session, "client", return_value=mock_client):
            url = await storage.get_file_url(
                "track.webm",
                operation="get_object",
                expires_in=3600,
                bucket="override-bucket",
            )

            assert "track.webm" in url
            mock_client.generate_presigned_url.assert_called_once()
            call_kwargs = mock_client.generate_presigned_url.call_args
            params = call_kwargs[1].get("Params") or call_kwargs[0][1]
            assert params["Bucket"] == "override-bucket"
            assert params["Key"] == "track.webm"


def test_get_source_storage_daily_default_region():
    """Daily platform without region falls back to us-east-1."""
    with patch("reflector.storage.settings") as mock_settings:
        mock_settings.DAILYCO_STORAGE_AWS_ACCESS_KEY_ID = "daily-key"
        mock_settings.DAILYCO_STORAGE_AWS_SECRET_ACCESS_KEY = "daily-secret"
        mock_settings.DAILYCO_STORAGE_AWS_BUCKET_NAME = "daily-bucket"
        mock_settings.DAILYCO_STORAGE_AWS_REGION = None

        from reflector.storage import get_source_storage

        storage = get_source_storage("daily")

        assert isinstance(storage, AwsStorage)
        assert storage._region == "us-east-1"


# --- Tests for get_dailyco_storage() ---


def test_get_dailyco_storage_with_role_arn():
    """get_dailyco_storage returns AwsStorage with role_arn for Daily API."""
    with patch("reflector.storage.settings") as mock_settings:
        mock_settings.DAILYCO_STORAGE_AWS_BUCKET_NAME = "daily-bucket"
        mock_settings.DAILYCO_STORAGE_AWS_REGION = "us-west-2"
        mock_settings.DAILYCO_STORAGE_AWS_ROLE_ARN = "arn:aws:iam::123:role/DailyAccess"

        from reflector.storage import get_dailyco_storage

        storage = get_dailyco_storage()

        assert isinstance(storage, AwsStorage)
        assert storage._bucket_name == "daily-bucket"
        assert storage._region == "us-west-2"
        assert storage._role_arn == "arn:aws:iam::123:role/DailyAccess"
        assert storage._access_key_id is None
        assert storage._secret_access_key is None


def test_get_dailyco_storage_no_conflict_when_access_keys_also_set():
    """get_dailyco_storage ignores access keys even when set (avoids mixed-auth error).

    This is the key regression test: DAILYCO_STORAGE_AWS_ACCESS_KEY_ID and
    SECRET_ACCESS_KEY are for get_source_storage(), not for get_dailyco_storage().
    """
    with patch("reflector.storage.settings") as mock_settings:
        mock_settings.DAILYCO_STORAGE_AWS_BUCKET_NAME = "daily-bucket"
        mock_settings.DAILYCO_STORAGE_AWS_REGION = "us-west-2"
        mock_settings.DAILYCO_STORAGE_AWS_ROLE_ARN = "arn:aws:iam::123:role/DailyAccess"
        # These are set for get_source_storage but must NOT leak into get_dailyco_storage
        mock_settings.DAILYCO_STORAGE_AWS_ACCESS_KEY_ID = "AKIA-worker-key"
        mock_settings.DAILYCO_STORAGE_AWS_SECRET_ACCESS_KEY = "worker-secret"

        from reflector.storage import get_dailyco_storage

        # Must NOT raise "cannot use both aws_role_arn and access keys"
        storage = get_dailyco_storage()

        assert isinstance(storage, AwsStorage)
        assert storage._role_arn == "arn:aws:iam::123:role/DailyAccess"
        assert storage._access_key_id is None
        assert storage._secret_access_key is None


def test_get_dailyco_storage_default_region():
    """get_dailyco_storage falls back to us-east-1 when region is None."""
    with patch("reflector.storage.settings") as mock_settings:
        mock_settings.DAILYCO_STORAGE_AWS_BUCKET_NAME = "daily-bucket"
        mock_settings.DAILYCO_STORAGE_AWS_REGION = None
        mock_settings.DAILYCO_STORAGE_AWS_ROLE_ARN = "arn:aws:iam::123:role/DailyAccess"

        from reflector.storage import get_dailyco_storage

        storage = get_dailyco_storage()

        assert storage._region == "us-east-1"


def test_get_dailyco_storage_raises_without_bucket():
    """get_dailyco_storage raises ValueError when bucket is not configured."""
    with patch("reflector.storage.settings") as mock_settings:
        mock_settings.DAILYCO_STORAGE_AWS_BUCKET_NAME = None

        from reflector.storage import get_dailyco_storage

        with pytest.raises(
            ValueError, match="DAILYCO_STORAGE_AWS_BUCKET_NAME required"
        ):
            get_dailyco_storage()


def test_get_dailyco_storage_exposes_role_credential():
    """get_dailyco_storage().role_credential returns the role ARN."""
    with patch("reflector.storage.settings") as mock_settings:
        mock_settings.DAILYCO_STORAGE_AWS_BUCKET_NAME = "daily-bucket"
        mock_settings.DAILYCO_STORAGE_AWS_REGION = "us-east-1"
        mock_settings.DAILYCO_STORAGE_AWS_ROLE_ARN = "arn:aws:iam::123:role/DailyAccess"

        from reflector.storage import get_dailyco_storage

        storage = get_dailyco_storage()

        assert storage.role_credential == "arn:aws:iam::123:role/DailyAccess"
        assert storage.bucket_name == "daily-bucket"
        assert storage.region == "us-east-1"


# --- Tests for get_whereby_storage() ---


def test_get_whereby_storage_with_access_keys():
    """get_whereby_storage returns AwsStorage with Whereby access keys."""
    whereby_settings = [
        ("WHEREBY_STORAGE_AWS_BUCKET_NAME", "whereby-bucket"),
        ("WHEREBY_STORAGE_AWS_REGION", "eu-west-1"),
        ("WHEREBY_STORAGE_AWS_ACCESS_KEY_ID", "whereby-key"),
        ("WHEREBY_STORAGE_AWS_SECRET_ACCESS_KEY", "whereby-secret"),
    ]
    mock_settings = MagicMock()
    mock_settings.WHEREBY_STORAGE_AWS_BUCKET_NAME = "whereby-bucket"
    mock_settings.__iter__ = MagicMock(return_value=iter(whereby_settings))

    # Patch both settings references: __init__.py and base.py
    with (
        patch("reflector.storage.settings", mock_settings),
        patch("reflector.storage.base.settings", mock_settings),
    ):
        from reflector.storage import get_whereby_storage

        storage = get_whereby_storage()

        assert isinstance(storage, AwsStorage)
        assert storage._bucket_name == "whereby-bucket"
        assert storage._region == "eu-west-1"
        assert storage._access_key_id == "whereby-key"
        assert storage._secret_access_key == "whereby-secret"


def test_get_whereby_storage_raises_without_bucket():
    """get_whereby_storage raises ValueError when bucket is not configured."""
    with patch("reflector.storage.settings") as mock_settings:
        mock_settings.WHEREBY_STORAGE_AWS_BUCKET_NAME = None

        from reflector.storage import get_whereby_storage

        with pytest.raises(
            ValueError, match="WHEREBY_STORAGE_AWS_BUCKET_NAME required"
        ):
            get_whereby_storage()


# --- Tests for get_transcripts_storage() ---


def test_get_transcripts_storage_with_garage():
    """get_transcripts_storage returns AwsStorage configured for Garage (custom endpoint)."""
    garage_settings = [
        ("TRANSCRIPT_STORAGE_AWS_BUCKET_NAME", "reflector-media"),
        ("TRANSCRIPT_STORAGE_AWS_REGION", "garage"),
        ("TRANSCRIPT_STORAGE_AWS_ACCESS_KEY_ID", "GK-garage-key"),
        ("TRANSCRIPT_STORAGE_AWS_SECRET_ACCESS_KEY", "garage-secret"),
        ("TRANSCRIPT_STORAGE_AWS_ENDPOINT_URL", "http://garage:3900"),
    ]
    mock_settings = MagicMock()
    mock_settings.TRANSCRIPT_STORAGE_BACKEND = "aws"
    mock_settings.__iter__ = MagicMock(return_value=iter(garage_settings))

    with (
        patch("reflector.storage.settings", mock_settings),
        patch("reflector.storage.base.settings", mock_settings),
    ):
        from reflector.storage import get_transcripts_storage

        storage = get_transcripts_storage()

        assert isinstance(storage, AwsStorage)
        assert storage._bucket_name == "reflector-media"
        assert storage._endpoint_url == "http://garage:3900"
        assert storage._access_key_id == "GK-garage-key"
        assert storage.boto_config.s3["addressing_style"] == "path"


def test_get_transcripts_storage_with_vanilla_aws():
    """get_transcripts_storage returns AwsStorage configured for real AWS S3."""
    aws_settings = [
        ("TRANSCRIPT_STORAGE_AWS_BUCKET_NAME", "prod-transcripts"),
        ("TRANSCRIPT_STORAGE_AWS_REGION", "us-east-1"),
        ("TRANSCRIPT_STORAGE_AWS_ACCESS_KEY_ID", "AKIA-prod-key"),
        ("TRANSCRIPT_STORAGE_AWS_SECRET_ACCESS_KEY", "prod-secret"),
    ]
    mock_settings = MagicMock()
    mock_settings.TRANSCRIPT_STORAGE_BACKEND = "aws"
    mock_settings.__iter__ = MagicMock(return_value=iter(aws_settings))

    with (
        patch("reflector.storage.settings", mock_settings),
        patch("reflector.storage.base.settings", mock_settings),
    ):
        from reflector.storage import get_transcripts_storage

        storage = get_transcripts_storage()

        assert isinstance(storage, AwsStorage)
        assert storage._bucket_name == "prod-transcripts"
        assert storage._endpoint_url is None
        assert storage._access_key_id == "AKIA-prod-key"


# --- Tests for coexistence (selfhosted scenario) ---


def test_all_factories_coexist_selfhosted_scenario():
    """All storage factories work simultaneously with selfhosted config.

    Simulates the real selfhosted setup:
    - Transcript storage → Garage (http://garage:3900)
    - Daily API storage → role_arn (for Daily to write recordings)
    - Source storage → access keys (for workers to read Daily's S3 bucket)
    """
    transcript_settings = [
        ("TRANSCRIPT_STORAGE_AWS_BUCKET_NAME", "reflector-media"),
        ("TRANSCRIPT_STORAGE_AWS_REGION", "garage"),
        ("TRANSCRIPT_STORAGE_AWS_ACCESS_KEY_ID", "GK-garage-key"),
        ("TRANSCRIPT_STORAGE_AWS_SECRET_ACCESS_KEY", "garage-secret"),
        ("TRANSCRIPT_STORAGE_AWS_ENDPOINT_URL", "http://garage:3900"),
    ]
    mock_settings = MagicMock()
    # Transcript storage: Garage
    mock_settings.TRANSCRIPT_STORAGE_BACKEND = "aws"
    mock_settings.__iter__ = MagicMock(return_value=iter(transcript_settings))

    # Daily.co: both role_arn AND access keys configured
    mock_settings.DAILYCO_STORAGE_AWS_BUCKET_NAME = "daily-recordings"
    mock_settings.DAILYCO_STORAGE_AWS_REGION = "us-west-2"
    mock_settings.DAILYCO_STORAGE_AWS_ROLE_ARN = "arn:aws:iam::123:role/DailyAccess"
    mock_settings.DAILYCO_STORAGE_AWS_ACCESS_KEY_ID = "AKIA-daily-worker"
    mock_settings.DAILYCO_STORAGE_AWS_SECRET_ACCESS_KEY = "daily-worker-secret"

    with (
        patch("reflector.storage.settings", mock_settings),
        patch("reflector.storage.base.settings", mock_settings),
    ):
        from reflector.storage import (
            get_dailyco_storage,
            get_source_storage,
            get_transcripts_storage,
        )

        # 1. Transcript storage → Garage
        transcript_storage = get_transcripts_storage()
        assert transcript_storage._endpoint_url == "http://garage:3900"
        assert transcript_storage._access_key_id == "GK-garage-key"

        # 2. Daily API storage → role_arn only (no access keys)
        daily_api_storage = get_dailyco_storage()
        assert daily_api_storage._role_arn == "arn:aws:iam::123:role/DailyAccess"
        assert daily_api_storage._access_key_id is None

        # 3. Source storage → access keys only (no role_arn)
        source_storage = get_source_storage("daily")
        assert source_storage._access_key_id == "AKIA-daily-worker"
        assert source_storage._role_arn is None
        assert source_storage._endpoint_url is None
