"""Tests for remote state backends — GCS, S3, Azure Blob, TFC."""

from __future__ import annotations

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

import pytest

from complyform.core.errors import (
    RemoteStateAuthError,
    RemoteStateNotFoundError,
    RemoteStateUnsupportedError,
)
from complyform.core.scanner.remote_backends import (
    _parse_azure_uri,
    _parse_gs_uri,
    _parse_s3_uri,
    _parse_tfc_uri,
    fetch_remote_state,
)

# ---------------------------------------------------------------------------
# URI parsing
# ---------------------------------------------------------------------------


class TestURIParsing:
    def test_parse_gs_uri(self) -> None:
        bucket, path = _parse_gs_uri("gs://my-bucket/path/to/state.tfstate")
        assert bucket == "my-bucket"
        assert path == "path/to/state.tfstate"

    def test_parse_s3_uri(self) -> None:
        bucket, path = _parse_s3_uri("s3://my-bucket/state.tfstate")
        assert bucket == "my-bucket"
        assert path == "state.tfstate"

    def test_parse_azure_uri(self) -> None:
        account, container, path = _parse_azure_uri(
            "azure://myaccount/mycontainer/state.tfstate"
        )
        assert account == "myaccount"
        assert container == "mycontainer"
        assert path == "state.tfstate"

    def test_parse_tfc_uri(self) -> None:
        org, workspace = _parse_tfc_uri("app.terraform.io/myorg/myworkspace")
        assert org == "myorg"
        assert workspace == "myworkspace"

    def test_parse_tfc_uri_with_https(self) -> None:
        org, workspace = _parse_tfc_uri("https://app.terraform.io/myorg/myworkspace")
        assert org == "myorg"
        assert workspace == "myworkspace"


# ---------------------------------------------------------------------------
# URI dispatch
# ---------------------------------------------------------------------------


class TestURIDispatch:
    @pytest.mark.parametrize(
        ("uri", "expected_func"),
        [
            ("gs://bucket/path", "fetch_gcs_state"),
            ("s3://bucket/path", "fetch_s3_state"),
            (
                "azure://account/container/path",
                "fetch_azure_state",
            ),
            ("app.terraform.io/org/ws", "fetch_tfc_state"),
        ],
    )
    async def test_uri_scheme_dispatch(self, uri: str, expected_func: str) -> None:
        mod = "complyform.core.scanner.remote_backends"
        with patch(
            f"{mod}.{expected_func}",
            new_callable=AsyncMock,
            return_value=b'{"version": 4}',
        ) as mock_fn:
            result = await fetch_remote_state(uri)
            assert result == b'{"version": 4}'
            mock_fn.assert_called_once()

    async def test_unsupported_scheme(self) -> None:
        with pytest.raises(RemoteStateUnsupportedError) as exc:
            await fetch_remote_state("ftp://server/path")
        assert "ftp" in exc.value.message


# ---------------------------------------------------------------------------
# GCS
# ---------------------------------------------------------------------------


class TestGCSBackend:
    async def test_fetch_gcs_state_success(self) -> None:
        from complyform.core.scanner.remote_backends import (
            fetch_gcs_state,
        )

        mock_blob = MagicMock()
        mock_blob.download_as_bytes.return_value = b'{"v": 4}'
        mock_bucket = MagicMock()
        mock_bucket.blob.return_value = mock_blob
        mock_client_inst = MagicMock()
        mock_client_inst.bucket.return_value = mock_bucket

        mock_storage = MagicMock()
        mock_storage.Client.return_value = mock_client_inst

        with patch.dict(
            sys.modules,
            {"google.cloud.storage": mock_storage},
        ):
            result = await fetch_gcs_state("my-bucket", "state.tfstate", None)

        assert result == b'{"v": 4}'
        mock_bucket.blob.assert_called_once_with("state.tfstate")

    async def test_fetch_gcs_state_not_found(self) -> None:
        from complyform.core.scanner.remote_backends import (
            fetch_gcs_state,
        )

        mock_blob = MagicMock()
        mock_blob.download_as_bytes.side_effect = Exception("404 Not Found")
        mock_bucket = MagicMock()
        mock_bucket.blob.return_value = mock_blob
        mock_client_inst = MagicMock()
        mock_client_inst.bucket.return_value = mock_bucket

        mock_storage = MagicMock()
        mock_storage.Client.return_value = mock_client_inst

        with (
            patch.dict(
                sys.modules,
                {"google.cloud.storage": mock_storage},
            ),
            pytest.raises(RemoteStateNotFoundError),
        ):
            await fetch_gcs_state("my-bucket", "missing.tfstate", None)

    async def test_fetch_gcs_state_auth_error(self) -> None:
        from complyform.core.scanner.remote_backends import (
            fetch_gcs_state,
        )

        mock_blob = MagicMock()
        mock_blob.download_as_bytes.side_effect = Exception("403 Forbidden credential")
        mock_bucket = MagicMock()
        mock_bucket.blob.return_value = mock_blob
        mock_client_inst = MagicMock()
        mock_client_inst.bucket.return_value = mock_bucket

        mock_storage = MagicMock()
        mock_storage.Client.return_value = mock_client_inst

        with (
            patch.dict(
                sys.modules,
                {"google.cloud.storage": mock_storage},
            ),
            pytest.raises(RemoteStateAuthError),
        ):
            await fetch_gcs_state("my-bucket", "state.tfstate", None)


# ---------------------------------------------------------------------------
# S3
# ---------------------------------------------------------------------------


class TestS3Backend:
    async def test_fetch_s3_state_success(self) -> None:
        from complyform.core.scanner.remote_backends import (
            fetch_s3_state,
        )

        mock_body = MagicMock()
        mock_body.read.return_value = b'{"version": 4}'
        mock_s3_client = MagicMock()
        mock_s3_client.get_object.return_value = {
            "Body": mock_body,
        }

        mock_boto3 = MagicMock()
        mock_boto3.client.return_value = mock_s3_client

        # Mock botocore.exceptions.ClientError
        mock_botocore_exc = MagicMock()

        with patch.dict(
            sys.modules,
            {
                "boto3": mock_boto3,
                "botocore": MagicMock(),
                "botocore.exceptions": mock_botocore_exc,
            },
        ):
            result = await fetch_s3_state("my-bucket", "state.tfstate")

        assert result == b'{"version": 4}'

    async def test_fetch_s3_state_not_found(self) -> None:
        from complyform.core.scanner.remote_backends import (
            fetch_s3_state,
        )

        # Create a real-looking ClientError class for mock
        class MockClientError(Exception):
            def __init__(self, error_response, operation):
                self.response = error_response
                super().__init__(str(error_response))

        mock_s3_client = MagicMock()
        mock_s3_client.get_object.side_effect = MockClientError(
            {"Error": {"Code": "NoSuchKey"}}, "GetObject"
        )

        mock_boto3 = MagicMock()
        mock_boto3.client.return_value = mock_s3_client

        # Mock botocore.exceptions.ClientError
        mock_botocore_exc = MagicMock()
        mock_botocore_exc.ClientError = MockClientError

        with (
            patch.dict(
                sys.modules,
                {
                    "boto3": mock_boto3,
                    "botocore": MagicMock(),
                    "botocore.exceptions": mock_botocore_exc,
                },
            ),
            pytest.raises(RemoteStateNotFoundError),
        ):
            await fetch_s3_state("my-bucket", "missing.tfstate")


# ---------------------------------------------------------------------------
# TFC
# ---------------------------------------------------------------------------


class TestTFCBackend:
    async def test_fetch_tfc_state_success(self) -> None:
        import httpx

        from complyform.core.scanner.remote_backends import (
            fetch_tfc_state,
        )

        dummy_req = httpx.Request("GET", "https://x.io")
        responses = [
            httpx.Response(
                200,
                json={"data": {"id": "ws-123"}},
                request=dummy_req,
            ),
            httpx.Response(
                200,
                json={
                    "data": {
                        "attributes": {
                            "hosted-state-download-url": (
                                "https://archivist.tf.io/v1/object/state"
                            ),
                        },
                    },
                },
                request=dummy_req,
            ),
            httpx.Response(
                200,
                content=b'{"version": 4}',
                request=dummy_req,
            ),
        ]
        call_idx = 0

        async def mock_get(self, url, **kwargs):
            nonlocal call_idx
            resp = responses[call_idx]
            call_idx += 1
            return resp

        with (
            patch.dict(
                "os.environ",
                {"TF_TOKEN": "test-token"},
            ),
            patch.object(httpx.AsyncClient, "get", mock_get),
        ):
            result = await fetch_tfc_state("org", "ws")
            assert result == b'{"version": 4}'

    async def test_fetch_tfc_state_missing_token(
        self,
    ) -> None:
        import os

        from complyform.core.scanner.remote_backends import (
            fetch_tfc_state,
        )

        with (
            patch.dict("os.environ", {}, clear=True),
            pytest.raises(RemoteStateAuthError) as exc,
        ):
            os.environ.pop("TF_TOKEN", None)
            await fetch_tfc_state("org", "ws")

        assert "TF_TOKEN" in exc.value.message


# ---------------------------------------------------------------------------
# Tier gating
# ---------------------------------------------------------------------------


class TestTierGating:
    def test_tier_gating_community_blocked(self) -> None:
        """Community tier cannot use remote backends."""
        from complyform.core.auth.tier_config import TIER_CONFIG

        community = TIER_CONFIG["community"]
        assert "gcs" not in community["scan_sources"]
        assert "s3" not in community["scan_sources"]

    def test_tier_gating_pro_allowed(self) -> None:
        """Pro tier can use remote state backends."""
        from complyform.core.auth.tier_config import TIER_CONFIG

        pro = TIER_CONFIG["pro"]
        assert "gcs" in pro["scan_sources"]
        assert "s3" in pro["scan_sources"]
        assert "azure_blob" in pro["scan_sources"]
        assert "tfc" in pro["scan_sources"]
