from unittest.mock import Mock

import pytest
from fastapi.security import HTTPAuthorizationCredentials
from jose.exceptions import JWTError
from pytest_mock import MockFixture
from src.__types.index import Tenant
from src.routers.management.authorizer import m2m_authorizer

M2M_PATH = "src.routers.management.authorizer.m2m_authorizer."


@pytest.fixture
def mock_connect_m2m(mocker: MockFixture) -> Mock:
    mock_connection = mocker.Mock()
    connect_mock = mocker.patch(f"{M2M_PATH}psycopg.connect")
    connect_mock.return_value.__enter__.return_value = mock_connection
    return connect_mock


class TestM2MAuthorizer:
    def test_get_m2m_tenant_success(
        self,
        monkeypatch: pytest.MonkeyPatch,
        mocker: MockFixture,
        credential: HTTPAuthorizationCredentials,
        mock_tenant: Tenant,
        mock_connect_m2m: Mock,
    ) -> None:
        """M2Mアクセストークンからテナント情報を取得できること"""
        monkeypatch.setattr(m2m_authorizer.settings, "management_m2m_pool_id", "ap-northeast-1_testpool")
        mocker.patch(f"{M2M_PATH}get_kid", return_value="kid-123")
        mocker.patch(f"{M2M_PATH}get_key", return_value=mocker.Mock())
        mocker.patch(
            f"{M2M_PATH}jwt.decode",
            return_value={"tenant_id": str(mock_tenant.id), "token_use": "access"},
        )
        repository = mocker.patch(f"{M2M_PATH}TenantRepository").return_value
        repository.get.return_value = mock_tenant

        tenant = m2m_authorizer.get_m2m_tenant(credential=credential)

        assert tenant == mock_tenant
        repository.get.assert_called_once_with(str(mock_tenant.id))
        mock_connect_m2m.assert_called_once()

    def test_get_m2m_tenant_raises_when_tenant_id_missing(
        self,
        monkeypatch: pytest.MonkeyPatch,
        mocker: MockFixture,
        credential: HTTPAuthorizationCredentials,
    ) -> None:
        """M2MアクセストークンにテナントIDがない場合、JWTErrorが発生すること"""
        monkeypatch.setattr(m2m_authorizer.settings, "management_m2m_pool_id", "ap-northeast-1_testpool")
        mocker.patch(f"{M2M_PATH}get_kid", return_value="kid-123")
        mocker.patch(f"{M2M_PATH}get_key", return_value=mocker.Mock())
        mocker.patch(
            f"{M2M_PATH}jwt.decode",
            return_value={"token_use": "access"},
        )

        with pytest.raises(JWTError, match="Missing required claim"):
            m2m_authorizer.get_m2m_tenant(credential=credential)

    def test_get_m2m_tenant_raises_when_issuer_not_configured(
        self,
        monkeypatch: pytest.MonkeyPatch,
        credential: HTTPAuthorizationCredentials,
    ) -> None:
        monkeypatch.setattr(m2m_authorizer.settings, "management_m2m_pool_id", None)

        with pytest.raises(ValueError, match="Management M2M issuer is not configured"):
            m2m_authorizer.get_m2m_tenant(credential=credential)
