"""Test different server responses."""
import json
import unittest
from uuid import UUID

import pytest

import moto.server as server
from moto import settings
from tests.test_redshiftdata.test_redshiftdata_constants import (
    DEFAULT_ENCODING,
    HttpHeaders,
)

CLIENT_ENDPOINT = "/"


@pytest.fixture(scope="function", autouse=True)
def skip_in_server_mode():
    if settings.TEST_SERVER_MODE:
        raise unittest.SkipTest("No point in testing this in ServerMode")


def headers(action):
    return {
        "X-Amz-Target": f"RedshiftData.{action}",
        "Content-Type": "application/x-amz-json-1.1",
    }


@pytest.fixture(autouse=True, name="client")
def fixture_client():
    backend = server.create_backend_app("redshift-data")
    yield backend.test_client()


def test_redshiftdata_cancel_statement_unknown_statement(client):
    statement_id = "890f1253-595b-4608-a0d1-73f933ccd0a0"
    response = client.post(
        CLIENT_ENDPOINT,
        data=json.dumps({"Id": statement_id}),
        headers=headers("CancelStatement"),
    )
    assert response.status_code == 400
    should_return_expected_exception(
        response, "ResourceNotFoundException", "Query does not exist."
    )


def test_redshiftdata_describe_statement_unknown_statement(client):
    statement_id = "890f1253-595b-4608-a0d1-73f933ccd0a0"
    response = client.post(
        CLIENT_ENDPOINT,
        data=json.dumps({"Id": statement_id}),
        headers=headers("DescribeStatement"),
    )
    assert response.status_code == 400
    should_return_expected_exception(
        response, "ResourceNotFoundException", "Query does not exist."
    )


def test_redshiftdata_get_statement_result_unknown_statement(client):
    statement_id = "890f1253-595b-4608-a0d1-73f933ccd0a0"
    response = client.post(
        CLIENT_ENDPOINT,
        data=json.dumps({"Id": statement_id}),
        headers=headers("GetStatementResult"),
    )
    assert response.status_code == 400
    should_return_expected_exception(
        response, "ResourceNotFoundException", "Query does not exist."
    )


def test_redshiftdata_execute_statement_with_minimal_values(client):
    database = "database"
    sql = "sql"
    response = client.post(
        CLIENT_ENDPOINT,
        data=json.dumps({"Database": database, "Sql": sql}),
        headers=headers("ExecuteStatement"),
    )
    assert response.status_code == 200
    payload = get_payload(response)

    assert payload["ClusterIdentifier"] is None
    assert payload["Database"] == database
    assert payload["DbUser"] is None
    assert payload["SecretArn"] is None

    uuid_obj = UUID(payload["Id"], version=4)
    assert str(uuid_obj) == payload["Id"]


def test_redshiftdata_execute_statement_with_all_values(client):
    cluster = "cluster"
    database = "database"
    dbUser = "dbUser"
    sql = "sql"
    secretArn = "secretArn"

    response = client.post(
        CLIENT_ENDPOINT,
        data=json.dumps(
            {
                "ClusterIdentifier": cluster,
                "Database": database,
                "DbUser": dbUser,
                "Sql": sql,
                "SecretArn": secretArn,
            }
        ),
        headers=headers("ExecuteStatement"),
    )
    assert response.status_code == 200
    payload = get_payload(response)

    assert payload["ClusterIdentifier"] == cluster
    assert payload["Database"] == database
    assert payload["DbUser"] == dbUser
    assert payload["SecretArn"] == secretArn


def test_redshiftdata_execute_statement_and_describe_statement(client):
    cluster = "cluster"
    database = "database"
    dbUser = "dbUser"
    sql = "sql"
    secretArn = "secretArn"

    # ExecuteStatement
    execute_response = client.post(
        CLIENT_ENDPOINT,
        data=json.dumps(
            {
                "ClusterIdentifier": cluster,
                "Database": database,
                "DbUser": dbUser,
                "Sql": sql,
                "SecretArn": secretArn,
            }
        ),
        headers=headers("ExecuteStatement"),
    )
    assert execute_response.status_code == 200
    execute_payload = get_payload(execute_response)

    # DescribeStatement
    describe_response = client.post(
        CLIENT_ENDPOINT,
        data=json.dumps({"Id": execute_payload["Id"]}),
        headers=headers("DescribeStatement"),
    )
    assert describe_response.status_code == 200
    describe_payload = get_payload(execute_response)

    assert describe_payload["ClusterIdentifier"] == cluster
    assert describe_payload["Database"] == database
    assert describe_payload["DbUser"] == dbUser
    assert describe_payload["SecretArn"] == secretArn


def test_redshiftdata_execute_statement_and_get_statement_result(client):
    database = "database"
    sql = "sql"

    # ExecuteStatement
    execute_response = client.post(
        CLIENT_ENDPOINT,
        data=json.dumps({"Database": database, "Sql": sql}),
        headers=headers("ExecuteStatement"),
    )
    assert execute_response.status_code == 200
    execute_payload = get_payload(execute_response)

    # GetStatementResult
    statement_result_response = client.post(
        CLIENT_ENDPOINT,
        data=json.dumps({"Id": execute_payload["Id"]}),
        headers=headers("GetStatementResult"),
    )
    assert statement_result_response.status_code == 200
    statement_result_payload = get_payload(statement_result_response)
    assert statement_result_payload["TotalNumberRows"] == 3

    # columns
    assert len(statement_result_payload["ColumnMetadata"]) == 3
    assert statement_result_payload["ColumnMetadata"][0]["name"] == "Number"
    assert statement_result_payload["ColumnMetadata"][1]["name"] == "Street"
    assert statement_result_payload["ColumnMetadata"][2]["name"] == "City"

    # records
    assert len(statement_result_payload["Records"]) == 3
    assert statement_result_payload["Records"][0][0]["longValue"] == 10
    assert statement_result_payload["Records"][1][1]["stringValue"] == "Beta st"
    assert statement_result_payload["Records"][2][2]["stringValue"] == "Seattle"


def test_redshiftdata_execute_statement_and_cancel_statement(client):
    database = "database"
    sql = "sql"

    # ExecuteStatement
    execute_response = client.post(
        CLIENT_ENDPOINT,
        data=json.dumps({"Database": database, "Sql": sql}),
        headers=headers("ExecuteStatement"),
    )
    assert execute_response.status_code == 200
    execute_payload = get_payload(execute_response)

    # CancelStatement 1
    cancel_response1 = client.post(
        CLIENT_ENDPOINT,
        data=json.dumps({"Id": execute_payload["Id"]}),
        headers=headers("CancelStatement"),
    )
    assert cancel_response1.status_code == 200
    cancel_payload1 = get_payload(cancel_response1)
    assert cancel_payload1["Status"] is True

    # CancelStatement 2
    cancel_response2 = client.post(
        CLIENT_ENDPOINT,
        data=json.dumps({"Id": execute_payload["Id"]}),
        headers=headers("CancelStatement"),
    )
    assert cancel_response2.status_code == 400
    should_return_expected_exception(
        cancel_response2,
        "ValidationException",
        (
            "Could not cancel a query that is already in ABORTED state "
            f"with ID: {execute_payload['Id']}"
        ),
    )


def get_payload(response):
    return json.loads(response.data.decode(DEFAULT_ENCODING))


def should_return_expected_exception(response, expected_exception, message):
    result_data = get_payload(response)
    assert response.headers.get(HttpHeaders.ErrorType) == expected_exception
    assert result_data["message"] == message
