import os
import pathlib
from types import SimpleNamespace

import pytest
from pytest import fixture

import fbnconfig
from fbnconfig import drive
from tests.integration.generate_test_name import gen


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

    env = os.environ["LUSID_ENV"]
    token = os.environ["FBN_ACCESS_TOKEN"]
    return SimpleNamespace(env=env, token=token)


@fixture
def client(lusid_env):
    return fbnconfig.create_client(lusid_env.env, lusid_env.token)


@fixture
def deployment_name():
    return gen("drive")


def resources(deployment_name):
    base_folder = deployment_name
    f1 = drive.FolderResource(id="base_folder", name=base_folder, parent=drive.root)
    f2 = drive.FolderResource(id="sub_folder", name="subfolder", parent=f1)
    f3 = drive.FolderResource(id="sub_sub_folder", name="subfolder2", parent=f2)
    content_path = pathlib.Path(__file__).parent.resolve() / pathlib.Path("poem.txt")
    ff = drive.FileResource(id="file1", folder=f3, name="myfile.txt", content_path=content_path)
    return {r.id: r for r in [f1, f2, f3, ff]}


@fixture
def deployment(deployment_name, lusid_env):
    res = resources(deployment_name)
    print(f"\nRunning for deployment {deployment_name}...")
    yield fbnconfig.Deployment(deployment_name, list(res.values()))
    print("\nTearing down resources...")
    fbnconfig.deployex(fbnconfig.Deployment(deployment_name, []), lusid_env.env, lusid_env.token)


def test_teardown(deployment, client, lusid_env):
    fbnconfig.deployex(deployment, lusid_env.env, lusid_env.token)
    fbnconfig.deployex(fbnconfig.Deployment(deployment.id, []), lusid_env.env, lusid_env.token)
    search = client.post("/drive/api/search/", json={"withPath": "/", "name": deployment.id})
    assert search.json()["values"] == []


def test_create(deployment, client, lusid_env):
    fbnconfig.deployex(deployment, lusid_env.env, lusid_env.token)
    search = client.post("/drive/api/search/", json={"withPath": "/", "name": deployment.id})
    assert len(search.json()["values"]) == 1


def test_update(deployment, lusid_env, client):
    fbnconfig.deployex(deployment, lusid_env.env, lusid_env.token)
    search = client.post("/drive/api/search/", json={"withPath": "/", "name": deployment.id})
    assert len(search.json()["values"]) == 1


@pytest.mark.asyncio
async def test_file_fetch(deployment, lusid_env):
    # GIVEN File is deployed
    fbnconfig.deployex(deployment, lusid_env.env, lusid_env.token)
    deployment_name = deployment.id
    client = fbnconfig.create_client(lusid_env.env, lusid_env.token, is_async=True)
    search = await client.post(
        "/drive/api/search/",
        json={"withPath": f"/{deployment_name}/subfolder/subfolder2", "name": "myfile.txt"}
    )
    drive_id = search.json()["values"][0]["id"]
    # WHEN the fetch method is called for that resource
    response = await drive.FileResource.fetch(
        client=client,
        keys={
            "driveId": drive_id,
        }
    )
    # THEN the response is as expected
    expected_response = {
        "id": f"file_{drive_id}".lower(),
        "driveId": drive_id,
        "folder": {
            "$ref": f"folder_/{deployment_name}/subfolder/subfolder2"
        },
        "name": "myfile.txt",
        "contentPath": f"/{deployment_name}/subfolder/subfolder2/myfile.txt",
    }
    assert response == expected_response


@pytest.mark.asyncio
async def test_folder_list_root_folder(deployment, lusid_env):
    # GIVEN Folder is deployed
    fbnconfig.deployex(deployment, lusid_env.env, lusid_env.token)
    deployment_name = deployment.id
    client = fbnconfig.create_client(lusid_env.env, lusid_env.token, is_async=True)
    search = await client.post(
        "/drive/api/search/",
        json={"withPath": "/", "name": deployment_name}
    )
    drive_id = search.json()["values"][0]["id"]
    # WHEN the list method is called for that resource
    response = await drive.FolderResource.list(
        client=client,
        query_params={"filter": f"name eq '{deployment_name}'"},
        path_params={}
    )
    # THEN the response is as expected
    expected_response = {
        "values": [
            {
                "keys": {
                    "driveId": drive_id,
                },
                "value": {
                    "id": f"folder_/{deployment_name}",
                    "driveId": drive_id,
                    "name": deployment_name,
                    "parent": {
                        "$ref": drive.root.id
                    }
                }
            }
        ]
    }
    assert response == expected_response


@pytest.mark.asyncio
async def test_folder_list_not_root_folder(deployment, lusid_env):
    # GIVEN Folder is deployed
    fbnconfig.deployex(deployment, lusid_env.env, lusid_env.token)
    deployment_name = deployment.id
    client = fbnconfig.create_client(lusid_env.env, lusid_env.token, is_async=True)
    search = await client.post(
        "/drive/api/search/",
        json={"withPath": "/", "name": deployment_name}
    )
    parent_drive_id = search.json()["values"][0]["id"]
    # WHEN the list method is called for that resource
    response = await drive.FolderResource.list(
        client=client,
        query_params={"filter": "name eq 'subfolder'"},
        path_params={
            "driveId": parent_drive_id
        }
    )
    # THEN the response is as expected
    search = await client.post(
        "/drive/api/search/",
        json={"withPath": f"/{deployment_name}", "name": "subfolder"}
    )
    drive_id = search.json()["values"][0]["id"]
    expected_response = {
        "values": [
            {
                "keys": {
                    "driveId": drive_id,
                },
                "value": {
                    "id": f"folder_/{deployment_name}/subfolder",
                    "driveId": drive_id,
                    "name": "subfolder",
                    "parent": {
                        "$ref": f"folder_/{deployment_name}"
                    }
                }
            }
        ]
    }
    assert response == expected_response


@pytest.mark.asyncio
async def test_folder_fetch(deployment, lusid_env):
    # GIVEN Folder is deployed
    fbnconfig.deployex(deployment, lusid_env.env, lusid_env.token)
    deployment_name = deployment.id
    client = fbnconfig.create_client(lusid_env.env, lusid_env.token, is_async=True)
    search = await client.post(
        "/drive/api/search/",
        json={"withPath": f"/{deployment_name}/subfolder", "name": "subfolder2"}
    )
    drive_id = search.json()["values"][0]["id"]
    # WHEN the fetch method is called for that resource
    response = await drive.FolderResource.fetch(
        client=client,
        keys={
            "driveId": drive_id,
        }
    )
    # THEN the response is as expected
    expected_response = {
        "id": f"folder_/{deployment_name}/subfolder/subfolder2",
        "driveId": drive_id,
        "name": "subfolder2",
        "parent": {
            "$ref": f"folder_/{deployment_name}/subfolder"
        },
    }
    assert response == expected_response
