import os
from types import SimpleNamespace
from unittest.mock import ANY

import pytest
from httpx import HTTPStatusError

import fbnconfig
import tests.integration.schedule as schedule
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 = {}
client = fbnconfig.create_client(lusid_env, token)


@pytest.fixture(scope="module")
def setup_deployment():
    deployment_name = gen("schedule")
    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):
    # create first
    fbnconfig.deployex(schedule.configure(setup_deployment), lusid_env, token)
    fbnconfig.deployex(fbnconfig.Deployment(setup_deployment.name, []), lusid_env, token)
    try:
        client.get(f"/scheduler2/api/schedules/{setup_deployment.name}/python-sched")
    except HTTPStatusError as error:
        assert error.response.status_code == 404


def test_create(setup_deployment):
    fbnconfig.deployex(schedule.configure(setup_deployment), lusid_env, token)
    search = client.get(f"/scheduler2/api/schedules/{setup_deployment.name}/python-sched")
    assert search.status_code == 200


def test_update(setup_deployment):
    fbnconfig.deployex(schedule.configure(setup_deployment), lusid_env, token)
    fbnconfig.deployex(schedule.configure(setup_deployment), lusid_env, token)
    search = client.get(f"/scheduler2/api/schedules/{setup_deployment.name}/python-sched")
    assert search.status_code == 200


@pytest.mark.asyncio
async def test_schedule_list_with_filter(setup_deployment):
    # GIVEN Schedule is deployed
    fbnconfig.deployex(schedule.configure(setup_deployment), lusid_env, token)
    scope = setup_deployment.name
    client = fbnconfig.create_client(lusid_env, token, is_async=True)
    # WHEN calling list with filter for the deployment name
    result = await schedule.scheduler.ScheduleResource.list(
        client=client,
        query_params={"filter": f"scheduleIdentifier.scope eq '{scope}'"},
    )
    # THEN the result has correct structure
    expected_result = {
        "values": [
            {
                "keys": {
                    "scope": scope,
                    "code": "python-sched"
                },
                "value": {
                    "id": f"schedule_{scope}_python-sched".lower(),
                    "scope": scope,
                    "code": "python-sched",
                    "arguments": {
                        "arg1": "mynotdefaultvalue"
                    },
                    "author": "",
                    "description": "my awesome schedule",
                    "enabled": True,
                    "expression": "0 30 4 * * ? *",
                    "name": "my-schedule",
                    "owner": "",
                    "timezone": "Europe/London",
                    "useAsAuth": {
                        "$ref": ANY
                    },
                    "job": {
                        "$ref": f"job_{scope}_job".lower()
                    }
                }
            }
        ]
    }
    assert result["values"] == expected_result["values"]


@pytest.mark.asyncio
async def test_schedule_fetch(setup_deployment):
    # GIVEN Schedule is deployed
    fbnconfig.deployex(schedule.configure(setup_deployment), lusid_env, token)
    scope = setup_deployment.name
    code = "python-sched"
    client = fbnconfig.create_client(lusid_env, token, is_async=True)
    # WHEN the fetch method is called for that resource
    response = await schedule.scheduler.ScheduleResource.fetch(
        client=client,
        keys={
            "scope": scope,
            "code": code,
        }
    )
    # THEN the response is as expected
    expected_response = {
        "id": f"schedule_{scope}_python-sched".lower(),
        "scope": scope,
        "code": "python-sched",
        "arguments": {
            "arg1": "mynotdefaultvalue"
        },
        "author": "",
        "description": "my awesome schedule",
        "enabled": True,
        "expression": "0 30 4 * * ? *",
        "name": "my-schedule",
        "owner": "",
        "timezone": "Europe/London",
        "useAsAuth": {
            "$ref": ANY
        },
        "job": {
            "$ref": f"job_{scope}_job".lower()
        }
    }
    assert response == expected_response


@pytest.mark.asyncio
async def test_image_fetch(setup_deployment):
    # GIVEN Image is deployed
    fbnconfig.deployex(schedule.configure(setup_deployment), lusid_env, token)
    dest_name = "beany"
    dest_tag = "v6.0"
    client = fbnconfig.create_client(lusid_env, token, is_async=True)
    # WHEN the fetch method is called for that resource
    response = await schedule.scheduler.ImageResource.fetch(
        client=client,
        keys={
            "destName": dest_name,
            "destTag": dest_tag,
        }
    )
    # THEN the response is as expected
    expected_response = {
        "id": f"image_{dest_name}_{dest_tag}".lower(),
        "destName": dest_name,
        "destTag": dest_tag,
        "sourceImage": f"{dest_name}:{dest_tag}"
    }
    assert response == expected_response


@pytest.mark.asyncio
async def test_job_list_with_filter(setup_deployment):
    # GIVEN Job is deployed
    fbnconfig.deployex(schedule.configure(setup_deployment), lusid_env, token)
    scope = setup_deployment.name
    code = "job"
    client = fbnconfig.create_client(lusid_env, token, is_async=True)
    # WHEN calling list with filter for the deployment name
    result = await schedule.scheduler.JobResource.list(
        client=client,
        query_params={"filter": f"jobId.scope eq '{scope}' and jobId.code eq '{code}'"},
    )
    # THEN the result has correct structure
    expected_result = {
        "values": [
            {
                "keys": {
                    "scope": scope,
                    "code": code
                },
                "value": {
                    "id": f"job_{scope}_{code}".lower(),
                    "scope": scope,
                    "code": code,
                    "commandLineArgumentSeparator": " ",
                    "dateCreated": ANY,
                    "description": "something nice",
                    "author": "",
                    "maxCpu": "2",
                    "maxMemory": "200Mi",
                    "minCpu": "1",
                    "minMemory": "100Mi",
                    "ttl": 300,
                    "image": {
                        "$ref": "image_beany_v6.0".lower()
                    },
                    "name": "RobsJob",
                    "argumentDefinitions": {
                        "arg1": {
                            "constraints": "None",
                            "dataType": "String",
                            "defaultValue": "mydefaultvalue",
                            "description": "My argument",
                            "order": 1,
                            "passedAs": "EnvironmentVariable",
                            "required": False
                        }
                    },
                }
            }
        ]
    }
    assert result["values"] == expected_result["values"]
