import os
from types import SimpleNamespace

import pytest
from httpx import HTTPStatusError
from pytest import fixture

import fbnconfig
import tests.integration.configuration as configuration
from tests.integration.generate_test_name import gen

if os.getenv("FBN_ACCESS_TOKEN") is None or os.getenv("LUSID_ENV") is None:
    raise (
        RuntimeError("Both FBN_ACCESS_TOKEN and LUSID_ENV variables need to be set to run these tests")
    )

lusid_env = os.environ["LUSID_ENV"]
token = os.environ["FBN_ACCESS_TOKEN"]
host_vars = {}


@fixture
def setup_deployment():
    deployment_name = gen("configuration")
    print(f"\nRunning for deployment {deployment_name}...")
    yield SimpleNamespace(name=deployment_name)
    # Teardown: Clean up resources (if any) after the test
    print("\nTearing down resources...")
    fbnconfig.deployex(fbnconfig.Deployment(deployment_name, []), lusid_env, token)


def test_teardown(setup_deployment):
    # set it up
    fbnconfig.deployex(configuration.configure(setup_deployment), lusid_env, token)

    # Tear it down
    fbnconfig.deployex(fbnconfig.Deployment(setup_deployment.name, []), lusid_env, token)
    client = fbnconfig.create_client(lusid_env, token)
    try:
        client.request("get", "/configuration/api/sets/personal/robTest/rbCode")
    except HTTPStatusError as error:
        # check it was deleted
        assert error.response.status_code == 404


def test_create(setup_deployment):
    fbnconfig.deployex(configuration.configure(setup_deployment), lusid_env, token)
    client = fbnconfig.create_client(lusid_env, token)
    item = client.request(
        "get", f"/configuration/api/sets/personal/set1-{setup_deployment.name}/rbCode/items/username"
    )
    assert item.status_code == 200


def test_update(setup_deployment):
    fbnconfig.deployex(configuration.configure(setup_deployment), lusid_env, token)
    client = fbnconfig.create_client(lusid_env, token)
    item = client.request(
        "get", f"/configuration/api/sets/personal/set1-{setup_deployment.name}/rbCode/items/username"
    )

    assert item.status_code == 200


@pytest.mark.asyncio
async def test_item_list(setup_deployment):
    # GIVEN Item is deployed
    fbnconfig.deployex(configuration.configure(setup_deployment), lusid_env, token)
    type = "shared"
    scope = f"shared-{setup_deployment.name}"
    code = "testref2"
    key = "user"
    client = fbnconfig.create_client(lusid_env, token, is_async=True)
    # WHEN the fetch method is called for that resource
    response = await configuration.configuration.ItemResource.list(
        client=client,
        path_params={
            "setType": type,
            "setScope": scope,
            "setCode": code,
        }
    )
    # THEN the response is as expected
    expected_response = {
        "values": [
            {
                "keys": {
                    "setType": type,
                    "setScope": scope,
                    "setCode": code,
                    "key": key
                },
                "value": {
                    "id": f"item_{type}_{scope}_{code}_{key}".lower(),
                    "description": "something nice",
                    "key": key,
                    "value": "lucy-login",
                    "valueType": "text",
                    "isSecret": False,
                    "ref": f"config://{type}/{scope}/{code}/{key}",
                    "blockReveal": False,
                    "set": {
                        "$ref": f"set_{type}_{scope}_{code}".lower()
                    }
                }
            }
        ]
    }
    assert response == expected_response


@pytest.mark.asyncio
async def test_item_fetch(setup_deployment):
    # GIVEN Item is deployed
    fbnconfig.deployex(configuration.configure(setup_deployment), lusid_env, token)
    set_type = "shared"
    set_scope = f"shared-{setup_deployment.name}"
    set_code = "testref2"
    key = "user"
    client = fbnconfig.create_client(lusid_env, token, is_async=True)
    # WHEN the fetch method is called for that resource
    response = await configuration.configuration.ItemResource.fetch(
        client=client,
        keys={
            "setType": set_type,
            "setScope": set_scope,
            "setCode": set_code,
            "key": key
        }
    )
    # THEN the response is as expected
    expected_response = {
        "id": f"item_{set_type}_{set_scope}_{set_code}_{key}".lower(),
        "description": "Lucy test",
        "key": key,
        "value": "lucy-login",
        "valueType": "text",
        "isSecret": False,
        "ref": f"config://{set_type}/{set_scope}/{set_code}/{key}",
        "blockReveal": False,
        "set": {
            "$ref": f"set_{set_type}_{set_scope}_{set_code}".lower()
        }
    }
    assert response == expected_response


@pytest.mark.asyncio
async def test_set_list(setup_deployment):
    # GIVEN Set is deployed
    fbnconfig.deployex(configuration.configure(setup_deployment), lusid_env, token)
    client = fbnconfig.create_client(lusid_env, token, is_async=True)
    # WHEN the fetch method is called for that resource
    response = await configuration.configuration.SetResource.list(
        client=client,
        path_params={}
    )
    # THEN the response is as expected
    assert "values" in response and isinstance(response["values"], list)


@pytest.mark.asyncio
async def test_set_fetch(setup_deployment):
    # GIVEN Set is deployed
    fbnconfig.deployex(configuration.configure(setup_deployment), lusid_env, token)
    type = "shared"
    scope = f"shared-{setup_deployment.name}"
    code = "testref2"
    client = fbnconfig.create_client(lusid_env, token, is_async=True)
    # WHEN the fetch method is called for that resource
    response = await configuration.configuration.SetResource.fetch(
        client=client,
        keys={
            "type": type,
            "scope": scope,
            "code": code,
        }
    )
    # THEN the response is as expected
    expected_response = {
        "id": f"set_{type}_{scope}_{code}".lower(),
        "description": "something nice",
        "scope": scope,
        "code": code,
        "type": type,
    }
    assert response == expected_response
