import io
import json
from typing import Any
from unittest import mock

import pytest
from data_contracts.v3.entities import (
    GetCLMEntitiesInfoPayload,
    GetEntitiesPayload,
    GetFenXIdMappingsPayload,
)
from data_contracts.v3.entity.types import BusinessEntity
from data_contracts.v3.entity.types.base import EntityType
from platform_sdk.exceptions import (
    EntityServiceError,
    LambdaClientError,
    UserManagementError,
)
from platform_sdk.model.user_management import UserManagementMethods
from platform_sdk.utils.lambdas import (
    CrossAccountLambdaClient,
    EntityServiceLambdaClient,
    UserManagementLambdaClient,
)

DUMMY_TENANT_ID = "dummyTenant"
DUMMY_ENTITY_IDS = ["62bbe667-e67f-489d-9295-f198a4959bf3"]
DUMMY_LAMBDA_ARN = (
    "arn:aws:lambda:eu-central-1:111111111111:function:dev-user-management"
)
DUMMY_ROLE_ARN = "arn:aws:lambda:eu-central-1:111111111111:function:dev-user-management"
DUMMY_REQUEST_PAYLOAD = {
    "UserId": "12345",
    "Tenant": "dummyTenant",
    "Method": "DummyMethod",
}
DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD = {"statusCode": 200, "body": {}}
DUMMY_ENTITY_SERVICE_RESPONSE_PAYLOAD = {"status_code": 200, "body": {}}
DUMMY_RESPONSE_PAYLOAD_WITHOUT_BODY = {
    "statusCode": 200,
}
DUMMY_USER_ID = "2ef124a1-b0ae-4afb-8fcf-18cd159228dd"

# Sample responses
LIST_USERS_RESPONSE_PAYLOAD = {
    "statusCode": 200,
    "body": {
        "users": [
            {
                "user_id": "9c1d06f4-0cb2-4a27-a6af-1f080d2ad418",
                "role_id": "admin",
                "full_name": "Nima Yazdanmehr",
                "email": "nima.yazdanmehr@sentinels.ai",
            }
        ]
    },
}
GET_USER_ROLE_RESPONSE_PAYLOAD = {
    "statusCode": 200,
    "body": {"user_id": "9c1d06f4-0cb2-4a27-a6af-1f080d2ad418", "role_id": "admin"},
}
GET_USER_DETAILS_RESPONSE_PAYLOAD = {
    "statusCode": 200,
    "body": {
        "user_id": "9c1d06f4-0cb2-4a27-a6af-1f080d2ad418",
        "role_id": "admin",
        "full_name": "Nima Yazdanmehr",
        "email": "nima.yazdanmehr@sentinels.ai",
    },
}
GET_ENTITIES_RESPONSE_PAYLOAD = {
    "status_code": 200,
    "body": {
        "entities": [
            BusinessEntity(
                external_id="62bbe667-e67f-489d-9295-f198a4959bf3",
                name="DummyCompany",
                type=EntityType.BUSINESS,
            )
        ]
    },
}
GET_FENX_ID_MAPPINGS_RESPONSE_PAYLOAD = {
    "status_code": 200,
    "body": {"mappings": {"62bbe667-e67f-489d-9295-f198a4959bf3": "dummy-fenx-id-1"}},
}
GET_CLM_ENTITIES_RESPONSE_PAYLOAD = {
    "status_code": 200,
    "body": {
        "entities_info": [
            {
                "entity_id": "62bbe667-e67f-489d-9295-f198a4959bf3",
                "fenx_entity_id": "dummy-fenx-id-1",
                "hash": "dummy-hash",
                "full_name_hash": "dummy-full-name-hash",
                "status": "created",
                "changed_at": "2021-10-01T00:00:00",
            }
        ]
    },
}


@mock.patch("platform_sdk.utils.lambdas.GlobalSessionPool")
def test_cross_account_lambda_invoke_with_payload(mock_global_session_pool):
    mock_global_session_pool_instance = mock_global_session_pool.return_value
    mock_sts_boto3_session = mock.Mock()

    mock_sts_boto3_session.client().invoke.return_value = _get_invoke_response(
        200, 200, DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD
    )
    mock_global_session_pool_instance.get_session.return_value = mock_sts_boto3_session

    cross_account_lambda_client = CrossAccountLambdaClient(
        DUMMY_LAMBDA_ARN, DUMMY_ROLE_ARN
    )

    res = cross_account_lambda_client.invoke_request_response(DUMMY_REQUEST_PAYLOAD)

    mock_sts_boto3_session.client().invoke.assert_called_once_with(
        FunctionName=DUMMY_LAMBDA_ARN,
        Payload=CrossAccountLambdaClient._get_payload(DUMMY_REQUEST_PAYLOAD),
        InvocationType="RequestResponse",
        ClientContext="e30=",
    )

    assert res == DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD


@mock.patch("platform_sdk.utils.lambdas.GlobalSessionPool")
def test_cross_account_lambda_invoke_err(mock_global_session_pool):
    mock_global_session_pool_instance = mock_global_session_pool.return_value
    mock_lambda_client = mock.Mock()

    dummy_response = _get_invoke_response(
        400, 200, DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD
    )
    mock_lambda_client.invoke.return_value = dummy_response
    mock_session = mock_global_session_pool_instance.get_session.return_value
    mock_session.client.return_value = mock_lambda_client

    cross_account_lambda_client = CrossAccountLambdaClient(
        DUMMY_LAMBDA_ARN, DUMMY_ROLE_ARN
    )

    # Lazy initialization
    mock_session.client.get_lambda_client.assert_not_called()

    with pytest.raises(LambdaClientError) as error:
        cross_account_lambda_client.invoke_request_response(DUMMY_REQUEST_PAYLOAD)

    assert "400" in str(error.value)
    assert error.value.status_code == dummy_response["StatusCode"]
    assert error.value.response == dummy_response

    dummy_response2 = _get_invoke_response(
        200, 403, DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD
    )
    mock_lambda_client.invoke.return_value = dummy_response2

    with pytest.raises(LambdaClientError) as error:
        cross_account_lambda_client.invoke_request_response(DUMMY_REQUEST_PAYLOAD)

    assert "403" in str(error.value)
    assert error.value.status_code == dummy_response2["StatusCode"]
    assert error.value.response == dummy_response2

    # invalid lambda response
    mock_lambda_client.invoke.return_value = DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD

    with pytest.raises(LambdaClientError) as error:
        cross_account_lambda_client.invoke_request_response(DUMMY_REQUEST_PAYLOAD)

    assert str(DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD) in str(error.value)
    assert error.value.response == DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD
    assert (
        error.value.status_code is None
    )  # The payload does not contain a valid status code.


def test_cross_account_get_payload():
    payload = CrossAccountLambdaClient._get_payload(DUMMY_REQUEST_PAYLOAD)
    assert json.loads(payload) == DUMMY_REQUEST_PAYLOAD


@pytest.mark.parametrize("payload", [None, DUMMY_REQUEST_PAYLOAD])
def test_user_management_invoke_and_extract_body_without_payload(payload):
    mock_invoke_lambda = mock.Mock()
    user_management_lambda = UserManagementLambdaClient(
        DUMMY_LAMBDA_ARN, DUMMY_ROLE_ARN
    )

    user_management_lambda.invoke_request_response = mock_invoke_lambda

    mock_invoke_lambda.return_value = DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD

    res = user_management_lambda._invoke_and_get_valid_response(payload)

    assert res == DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD

    mock_invoke_lambda.assert_called_once_with(payload)


def test_user_management_list_users():
    mock_invoke_lambda = mock.Mock()
    user_management_lambda = UserManagementLambdaClient(
        DUMMY_LAMBDA_ARN, DUMMY_ROLE_ARN
    )

    user_management_lambda.invoke_request_response = mock_invoke_lambda
    mock_invoke_lambda.return_value = LIST_USERS_RESPONSE_PAYLOAD

    list_users = user_management_lambda.list_users(tenant_id=DUMMY_TENANT_ID)

    users_from_mock_response = LIST_USERS_RESPONSE_PAYLOAD["body"]["users"]
    assert len(list_users) == len(users_from_mock_response) == 1
    user_from_response = list_users[0]

    user_in_mock_response = users_from_mock_response[0]
    assert user_from_response.user_id == user_in_mock_response["user_id"]
    assert user_from_response.role_id == user_in_mock_response["role_id"]
    assert user_from_response.full_name == user_in_mock_response["full_name"]

    user_management_lambda.invoke_request_response.assert_called_once_with(
        UserManagementLambdaClient._get_event(
            UserManagementMethods.LIST_USERS,
            tenant_id=DUMMY_TENANT_ID,
            user_id=None,
        ),
    )


def test_user_management_get_user_details() -> None:
    mock_invoke_lambda = mock.Mock()
    user_management_lambda = UserManagementLambdaClient(
        DUMMY_LAMBDA_ARN, DUMMY_ROLE_ARN
    )

    user_management_lambda.invoke_request_response = mock_invoke_lambda
    mock_invoke_lambda.return_value = GET_USER_DETAILS_RESPONSE_PAYLOAD

    user_details = user_management_lambda.get_user_details(
        tenant_id=DUMMY_TENANT_ID, user_id=DUMMY_USER_ID
    )

    user_details_from_mock_response = GET_USER_DETAILS_RESPONSE_PAYLOAD["body"]
    assert user_details.user_id == user_details_from_mock_response["user_id"]
    assert user_details.role_id == user_details_from_mock_response["role_id"]
    assert user_details.full_name == user_details_from_mock_response["full_name"]
    assert user_details.email == user_details_from_mock_response["email"]

    user_management_lambda.invoke_request_response.assert_called_once_with(
        UserManagementLambdaClient._get_event(
            UserManagementMethods.GET_USER_DETAILS,
            tenant_id=DUMMY_TENANT_ID,
            user_id=DUMMY_USER_ID,
        )
    )


def test_user_management_get_user_role() -> None:
    mock_invoke_lambda = mock.Mock()
    user_management_lambda = UserManagementLambdaClient(
        DUMMY_LAMBDA_ARN, DUMMY_ROLE_ARN
    )

    user_management_lambda.invoke_request_response = mock_invoke_lambda
    mock_invoke_lambda.return_value = GET_USER_DETAILS_RESPONSE_PAYLOAD

    user_role = user_management_lambda.get_user_role(DUMMY_TENANT_ID, DUMMY_USER_ID)

    user_details_from_mock_response = GET_USER_DETAILS_RESPONSE_PAYLOAD["body"]
    assert user_role.user_id == user_details_from_mock_response["user_id"]
    assert user_role.role_id == user_details_from_mock_response["role_id"]

    user_management_lambda.invoke_request_response.assert_called_once_with(
        UserManagementLambdaClient._get_event(
            UserManagementMethods.GET_USER_ROLE,
            tenant_id=DUMMY_TENANT_ID,
            user_id=DUMMY_USER_ID,
        ),
    )


def test_user_management_errors() -> None:
    mock_invoke_lambda = mock.Mock()
    user_management_lambda = UserManagementLambdaClient(
        DUMMY_LAMBDA_ARN, DUMMY_ROLE_ARN
    )

    user_management_lambda.invoke_request_response = mock_invoke_lambda
    mock_invoke_lambda.return_value = DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD

    with pytest.raises(UserManagementError) as exc_info1:
        user_management_lambda.get_user_role(DUMMY_TENANT_ID, DUMMY_USER_ID)

    assert exc_info1.value.response == DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD
    assert (
        exc_info1.value.status_code
        == DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD["statusCode"]
    )

    with pytest.raises(UserManagementError) as exc_info2:
        user_management_lambda.get_user_details(DUMMY_TENANT_ID, DUMMY_USER_ID)
    assert exc_info2.value.response == DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD
    assert (
        exc_info2.value.status_code
        == DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD["statusCode"]
    )

    with pytest.raises(UserManagementError) as exc_info3:
        user_management_lambda.list_users(DUMMY_TENANT_ID)
    assert exc_info3.value.response == DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD
    assert (
        exc_info3.value.status_code
        == DUMMY_USER_MANAGEMENT_RESPONSE_PAYLOAD["statusCode"]
    )


def test_entity_service_get_entities() -> None:
    mock_invoke_lambda = mock.Mock()
    entity_service_client = EntityServiceLambdaClient(DUMMY_LAMBDA_ARN, DUMMY_ROLE_ARN)

    entity_service_client.invoke_request_response = mock_invoke_lambda
    mock_invoke_lambda.return_value = GET_ENTITIES_RESPONSE_PAYLOAD

    entities = entity_service_client.get_entities(DUMMY_TENANT_ID, DUMMY_ENTITY_IDS)

    assert entities == GET_ENTITIES_RESPONSE_PAYLOAD["body"]

    entity_service_client.invoke_request_response.assert_called_once_with(
        GetEntitiesPayload(tenant_id=DUMMY_TENANT_ID, external_ids=DUMMY_ENTITY_IDS)
    )


def test_entity_service_get_fenx_id_mappings() -> None:
    mock_invoke_lambda = mock.Mock()
    entity_service_client = EntityServiceLambdaClient(DUMMY_LAMBDA_ARN, DUMMY_ROLE_ARN)

    entity_service_client.invoke_request_response = mock_invoke_lambda
    mock_invoke_lambda.return_value = GET_FENX_ID_MAPPINGS_RESPONSE_PAYLOAD

    entities = entity_service_client.get_fenx_id_mappings(
        DUMMY_TENANT_ID, DUMMY_ENTITY_IDS
    )

    assert entities == GET_FENX_ID_MAPPINGS_RESPONSE_PAYLOAD["body"]

    entity_service_client.invoke_request_response.assert_called_once_with(
        GetFenXIdMappingsPayload(
            tenant_id=DUMMY_TENANT_ID, external_ids=DUMMY_ENTITY_IDS
        )
    )


def test_entity_service_get_clm_entities_info() -> None:
    entity_service_client = EntityServiceLambdaClient(DUMMY_LAMBDA_ARN, DUMMY_ROLE_ARN)
    entity_service_client.invoke_request_response = mock.Mock()
    entity_service_client.invoke_request_response.return_value = (
        GET_CLM_ENTITIES_RESPONSE_PAYLOAD
    )

    clm_entities_info_response = entity_service_client.get_clm_entities_info(
        DUMMY_TENANT_ID, DUMMY_ENTITY_IDS
    )
    response_as_dict = json.loads(clm_entities_info_response.json())

    assert response_as_dict == GET_CLM_ENTITIES_RESPONSE_PAYLOAD["body"]
    entity_service_client.invoke_request_response.assert_called_once_with(
        GetCLMEntitiesInfoPayload(
            tenant_id=DUMMY_TENANT_ID, external_ids=DUMMY_ENTITY_IDS
        )
    )


def test_entity_service_errors() -> None:
    mock_invoke_lambda = mock.Mock()
    entity_service = EntityServiceLambdaClient(DUMMY_LAMBDA_ARN, DUMMY_ROLE_ARN)

    entity_service.invoke_request_response = mock_invoke_lambda
    mock_invoke_lambda.return_value = DUMMY_ENTITY_SERVICE_RESPONSE_PAYLOAD

    with pytest.raises(EntityServiceError) as exc_info1:
        entity_service.get_entities(DUMMY_TENANT_ID, DUMMY_ENTITY_IDS)

    assert exc_info1.value.response == DUMMY_ENTITY_SERVICE_RESPONSE_PAYLOAD
    assert (
        exc_info1.value.status_code
        == DUMMY_ENTITY_SERVICE_RESPONSE_PAYLOAD["status_code"]
    )

    with pytest.raises(EntityServiceError) as exc_info2:
        entity_service.get_fenx_id_mappings(DUMMY_TENANT_ID, DUMMY_ENTITY_IDS)
    assert exc_info2.value.response == DUMMY_ENTITY_SERVICE_RESPONSE_PAYLOAD
    assert (
        exc_info2.value.status_code
        == DUMMY_ENTITY_SERVICE_RESPONSE_PAYLOAD["status_code"]
    )


def _get_invoke_response(
    status_code: int, http_status_code: int, payload: dict
) -> dict[str, Any]:
    return {
        "StatusCode": status_code,
        "Payload": io.StringIO(json.dumps(payload)),
        "ResponseMetadata": {"HTTPStatusCode": http_status_code},
    }


def test_get_event():
    resp = UserManagementLambdaClient._get_event(
        UserManagementMethods.LIST_USERS, tenant_id="10"
    )
    assert resp == {"tenant_id": "10", "method": UserManagementMethods.LIST_USERS}
    resp = UserManagementLambdaClient._get_event(
        UserManagementMethods.LIST_USERS, tenant_id="10", user_id="20"
    )
    assert resp == {
        "user_id": "20",
        "tenant_id": "10",
        "method": UserManagementMethods.LIST_USERS,
    }
 solve these errors tests/unit/utils/test_lambdas.py:216: error: Value of type "object" is not indexable  [index]
tests/unit/utils/test_lambdas.py:249: error: Value of type "object" is not indexable  [index]
tests/unit/utils/test_lambdas.py:250: error: Value of type "object" is not indexable  [index]
tests/unit/utils/test_lambdas.py:251: error: Value of type "object" is not indexable  [index]
tests/unit/utils/test_lambdas.py:252: error: Value of type "object" is not indexable  [index]
tests/unit/utils/test_lambdas.py:275: error: Value of type "object" is not indexable  [index]
tests/unit/utils/test_lambdas.py:276: error: Value of type "object" is not indexable  [index]