import mock
import pytest
from copy import deepcopy
from boto3.dynamodb.conditions import Key

pytestmark = [
    pytest.mark.acceptance_cognito,
    pytest.mark.api,
    pytest.mark.data_mappers,
    pytest.mark.usefixtures("empty_data_mappers"),
]


@pytest.mark.auth
def test_auth(api_client_cognito, data_mapper_base_endpoint):
    headers = {"Authorization": None}
    assert (
        401
        == api_client_cognito.put(
            "{}/{}".format(data_mapper_base_endpoint, "a"), headers=headers
        ).status_code
    )
    assert (
        401
        == api_client_cognito.get(
            data_mapper_base_endpoint, headers=headers
        ).status_code
    )
    assert (
        401
        == api_client_cognito.delete(
            "{}/{}".format(data_mapper_base_endpoint, "a"), headers=headers
        ).status_code
    )


def test_it_creates_data_mapper(
    api_client_cognito,
    data_mapper_base_endpoint,
    data_mapper_table,
    glue_table_factory,
    stack,
):
    # Arrange
    table = glue_table_factory()
    key = "test"
    data_mapper = {
        "DataMapperId": key,
        "Columns": ["a"],
        "QueryExecutor": "athena",
        "QueryExecutorParameters": {
            "DataCatalogProvider": "glue",
            "Database": table["Database"],
            "Table": table["Table"],
        },
        "Format": "parquet",
        "RoleArn": "arn:aws:iam::123456789012:role/S3F2DataAccessRole",
        "DeleteOldVersions": False,
        "IgnoreObjectNotFoundExceptions": True,
    }
    # Act
    response = api_client_cognito.put(
        "{}/{}".format(data_mapper_base_endpoint, key), json=data_mapper
    )
    response_body = response.json()
    # Assert
    expected = deepcopy(data_mapper)
    expected["CreatedBy"] = {
        "Username": "aws-uk-sa-builders@amazon.com",
        "Sub": mock.ANY,
    }
    expected["DeleteOldVersions"] = False
    # Check the response is ok
    assert 201 == response.status_code
    assert expected == response_body
    # Check the item exists in the DDB Table
    query_result = data_mapper_table.query(
        KeyConditionExpression=Key("DataMapperId").eq(key)
    )
    assert 1 == len(query_result["Items"])
    assert expected == query_result["Items"][0]
    assert (
        response.headers.get("Access-Control-Allow-Origin")
        == stack["APIAccessControlAllowOriginHeader"]
    )


def test_it_modifies_data_mapper(
    api_client_cognito,
    data_mapper_base_endpoint,
    data_mapper_table,
    glue_table_factory,
    stack,
):
    # Arrange
    table = glue_table_factory()
    key = "test"
    data_mapper = {
        "DataMapperId": key,
        "Columns": ["a"],
        "QueryExecutor": "athena",
        "QueryExecutorParameters": {
            "DataCatalogProvider": "glue",
            "Database": table["Database"],
            "Table": table["Table"],
        },
        "Format": "parquet",
        "RoleArn": "arn:aws:iam::123456789012:role/S3F2DataAccessRole",
        "DeleteOldVersions": False,
        "IgnoreObjectNotFoundExceptions": False,
    }
    # Act
    create_response = api_client_cognito.put(
        "{}/{}".format(data_mapper_base_endpoint, key), json=data_mapper
    )
    data_mapper["Columns"] = ["b"]
    response = api_client_cognito.put(
        "{}/{}".format(data_mapper_base_endpoint, key), json=data_mapper
    )
    response_body = response.json()
    # Assert
    assert 201 == response.status_code
    assert response_body["Columns"] == ["b"]
    # Check the item exists in the DDB Table
    query_result = data_mapper_table.query(
        KeyConditionExpression=Key("DataMapperId").eq(key)
    )
    assert 1 == len(query_result["Items"])
    assert query_result["Items"][0]["Columns"] == ["b"]
    assert (
        response.headers.get("Access-Control-Allow-Origin")
        == stack["APIAccessControlAllowOriginHeader"]
    )


def test_it_creates_without_optionals(
    api_client_cognito,
    data_mapper_base_endpoint,
    data_mapper_table,
    glue_table_factory,
    stack,
):
    # Arrange
    table = glue_table_factory()
    key = "test"
    data_mapper = {
        "DataMapperId": key,
        "Columns": ["a"],
        "QueryExecutor": "athena",
        "QueryExecutorParameters": {
            "DataCatalogProvider": "glue",
            "Database": table["Database"],
            "Table": table["Table"],
        },
        "RoleArn": "arn:aws:iam::123456789012:role/S3F2DataAccessRole",
    }
    # Act
    response = api_client_cognito.put(
        "{}/{}".format(data_mapper_base_endpoint, key), json=data_mapper
    )
    response_body = response.json()
    # Assert
    expected = deepcopy(data_mapper)
    expected["Format"] = "parquet"
    expected["CreatedBy"] = {
        "Username": "aws-uk-sa-builders@amazon.com",
        "Sub": mock.ANY,
    }
    expected["DeleteOldVersions"] = True
    expected["IgnoreObjectNotFoundExceptions"] = False
    # Check the response is ok
    assert 201 == response.status_code
    assert expected == response_body
    # Check the item exists in the DDB Table
    query_result = data_mapper_table.query(
        KeyConditionExpression=Key("DataMapperId").eq(key)
    )
    assert 1 == len(query_result["Items"])
    assert expected == query_result["Items"][0]
    assert (
        response.headers.get("Access-Control-Allow-Origin")
        == stack["APIAccessControlAllowOriginHeader"]
    )


def test_it_rejects_invalid_data_mapper(
    api_client_cognito, data_mapper_base_endpoint, glue_table_factory, stack
):
    # Arrange
    table = glue_table_factory()
    key = "test"
    data_mapper = {
        "DataMapperId": key,
        "Columns": ["a"],
        "QueryExecutor": "athena",
        "QueryExecutorParameters": {
            "DataCatalogProvider": "glue",
            "Database": table["Database"],
            "Table": table["Table"],
        },
        "RoleArn": "arn:aws:iam::123456789012:role/WrongRoleName",
    }
    # Act
    response = api_client_cognito.put(
        "{}/{}".format(data_mapper_base_endpoint, key), json=data_mapper
    )
    response_body = response.json()
    # Assert
    assert 422 == response.status_code
    assert (
        response.headers.get("Access-Control-Allow-Origin")
        == stack["APIAccessControlAllowOriginHeader"]
    )


def test_it_rejects_invalid_role(api_client_cognito, data_mapper_base_endpoint, stack):
    key = "test"
    response = api_client_cognito.put(
        "{}/{}".format(data_mapper_base_endpoint, key), json={"INVALID": "PAYLOAD"}
    )
    assert 422 == response.status_code
    assert (
        response.headers.get("Access-Control-Allow-Origin")
        == stack["APIAccessControlAllowOriginHeader"]
    )


def test_it_rejects_invalid_data_source(
    api_client_cognito, data_mapper_base_endpoint, stack
):
    key = "test"
    response = api_client_cognito.put(
        "{}/{}".format(data_mapper_base_endpoint, key),
        json={
            "Columns": ["column"],
            "QueryExecutor": "unsupported",
            "QueryExecutorParameters": {},
            "Format": "parquet",
        },
    )
    assert 422 == response.status_code
    assert (
        response.headers.get("Access-Control-Allow-Origin")
        == stack["APIAccessControlAllowOriginHeader"]
    )


def test_it_rejects_invalid_data_catalog_provider(
    api_client_cognito, data_mapper_base_endpoint, stack
):
    key = "test"
    response = api_client_cognito.put(
        "{}/{}".format(data_mapper_base_endpoint, key),
        json={
            "Columns": ["column"],
            "QueryExecutor": "athena",
            "QueryExecutorParameters": {
                "Database": "database",
                "Table": "table",
                "DataCatalogProvider": "invalid",
            },
            "Format": "parquet",
            "RoleArn": "arn:aws:iam::123456789012:role/S3F2DataAccessRole",
        },
    )
    assert 422 == response.status_code
    assert (
        response.headers.get("Access-Control-Allow-Origin")
        == stack["APIAccessControlAllowOriginHeader"]
    )


def test_it_rejects_missing_glue_catalog(
    api_client_cognito, data_mapper_base_endpoint, stack
):
    key = "test"
    response = api_client_cognito.put(
        "{}/{}".format(data_mapper_base_endpoint, key),
        json={
            "Columns": ["column"],
            "QueryExecutor": "athena",
            "QueryExecutorParameters": {
                "Database": "non_existent",
                "Table": "non_existent",
                "DataCatalogProvider": "glue",
            },
            "Format": "parquet",
            "RoleArn": "arn:aws:iam::123456789012:role/S3F2DataAccessRole",
        },
    )
    assert 400 == response.status_code
    assert (
        response.headers.get("Access-Control-Allow-Origin")
        == stack["APIAccessControlAllowOriginHeader"]
    )


def test_it_gets_all_data_mappers(
    api_client_cognito, data_mapper_base_endpoint, glue_data_mapper_factory, stack
):
    # Arrange
    item = glue_data_mapper_factory()
    # Act
    response = api_client_cognito.get(data_mapper_base_endpoint)
    response_body = response.json()
    # Assert
    assert response.status_code == 200
    assert isinstance(response_body.get("DataMappers"), list)
    assert item in response_body["DataMappers"]
    assert (
        response.headers.get("Access-Control-Allow-Origin")
        == stack["APIAccessControlAllowOriginHeader"]
    )


def test_it_gets_data_mapper(
    api_client_cognito, data_mapper_base_endpoint, glue_data_mapper_factory, stack
):
    # Arrange
    item = glue_data_mapper_factory()
    key = item["DataMapperId"]
    # Act
    response = api_client_cognito.get("{}/{}".format(data_mapper_base_endpoint, key))
    response_body = response.json()
    # Assert
    assert response.status_code == 200
    assert item == response_body
    assert (
        response.headers.get("Access-Control-Allow-Origin")
        == stack["APIAccessControlAllowOriginHeader"]
    )


def test_it_deletes_data_mapper(
    api_client_cognito,
    glue_data_mapper_factory,
    data_mapper_base_endpoint,
    data_mapper_table,
    stack,
):
    # Arrange
    item = glue_data_mapper_factory()
    key = item["DataMapperId"]
    # Act
    response = api_client_cognito.delete("{}/{}".format(data_mapper_base_endpoint, key))
    # Assert
    assert 204 == response.status_code
    # Check the item doesn't exist in the DDB Table
    query_result = data_mapper_table.query(
        KeyConditionExpression=Key("DataMapperId").eq(key)
    )
    assert 0 == len(query_result["Items"])
    assert (
        response.headers.get("Access-Control-Allow-Origin")
        == stack["APIAccessControlAllowOriginHeader"]
    )
