import pytest
import utc
from webtest import TestApp

from mekansm import handlers
import mekansm.errors


def get_test_app(ctx):
    return TestApp(handlers.wsgi_app(ctx))


def dummy_context():
    return object()


def get_sample_deployment_data():
    return {
        "id": "d-1",
        "environment": "Production",
        "triggered_by": "Kappa",
        "repository": "mekansm",
        "owner": "dta",
        "sha": "Kappa",
        "account": "aws-account-id",
        "application": "dta-mekansm",
        "group": "dta-mekansm-Production",
        "s3location": "myartifacts/dta/Mekansm",
        "bundletype": "tgz",
        "config": "CodeDeployDefault.AllAtOnce"
    }


def test_kwargs_to_filter():
    got = handlers.kwargs_to_filter_obj(
        {"a", "b"},
        **{"filter[a]": "foo", "filter[b]": "bar"})
    assert got == {"a": "foo", "b": "bar"}


def test_invalid_kwargs_to_filter_obj_with_raises():
    with pytest.raises(mekansm.errors.MekansmInvalidFilters):
        handlers.kwargs_to_filter_obj(
            {"a", "b"},
            **{"filter[a]": "foo", "filter[c]": "bar"})


def test_root():
    app = get_test_app(dummy_context())
    resp = app.get("/", headers={"Accept": "application/vnd.api+json"})
    assert resp.status_int == 200
    assert resp.content_type == "application/json"
    assert {"deployments", "nodes"} < set(resp.json["links"].keys())


def test_nodes():

    class StubCtx:
        def get_nodes(self, *node_ids):
            return [
                {"id": "id-1", "datacenter": "d-1"},
                {"id": "id-2", "datacenter": "d-1"},
                {"id": "id-3", "datacenter": "d-1"}
            ]

        def get_node_ids(self, filter_obj):
            assert filter_obj == {"datacenter": "d-1"}
            return ["id-1", "id-2", "id-3"]

    app = get_test_app(StubCtx())
    resp = app.get("/nodes?filter[datacenter]=d-1")
    data = resp.json["data"]
    assert len(data) == 3
    node_data = data.pop(0)
    assert node_data["id"] == "id-1"
    assert node_data["attributes"] == {"datacenter": "d-1"}
    assert node_data["type"] == "nodes"


def test_nodes_with_invalid_filter():
    app = get_test_app(dummy_context())
    resp = app.get(
        "/nodes?filter[datacenter]=d-1&filter[Kappa]=Pogchamp",
        expect_errors=True)
    assert resp.status_int == 409


def test_missing_node_404s():

    class StubCtx:
        def get_node(self, node_id):
            assert node_id == "i-Kappa"
            raise mekansm.errors.NodeNotFound(node_id)

    app = TestApp(handlers.wsgi_app(StubCtx()))
    resp = app.get("/nodes/i-Kappa", expect_errors=True)
    assert resp.status_int == 404


def test_node():

    class StubCtx:
        def get_node(self, node_id):
            assert node_id == "i-1"
            return {"id": node_id, "datacenter": "d-1"}

    app = get_test_app(StubCtx())
    resp = app.get("/nodes/i-1")
    assert resp.status_int == 200
    node_data = resp.json["data"]
    assert node_data["id"] == "i-1"
    assert node_data["attributes"] == {"datacenter": "d-1"}
    assert node_data["type"] == "nodes"


@pytest.mark.parametrize(
    "url, expected_filter",
    [
        (
            "/nodes/i-1/deployments?filter[owner]=dta",
            {"node": "i-1", "owner": "dta"}
        ),
        (
            "/deployments?filter[owner]=dta",
            {"owner": "dta"}
        )
    ]
)
def test_deployments(url, expected_filter):

    class StubCtx:

        def get_deployments(self, filter_obj):
            assert filter_obj == expected_filter
            return [get_sample_deployment_data()]

    app = get_test_app(StubCtx())
    resp = app.get(url)
    assert resp.status_int == 200
    assert len(resp.json["data"]) == 1
    dep_data = resp.json["data"].pop()
    assert dep_data["id"] == "d-1"
    assert dep_data["type"] == "deployments"


@pytest.mark.parametrize(
    "url",
    [
        "/nodes/i-1/deployments?filter[owner]=dta",
        "/deployments?filter[owner]=dta",
    ]
)
def test_deployments_with_invalid_filter_raise(url):
    app = get_test_app(dummy_context())
    resp = app.get(
        "/nodes?filter[owner]=dta&filter[Kappa]=Pogchamp", expect_errors=True)
    assert resp.status_int == 409


def test_missing_deployment_404s():

    class StubCtx:
        def get_deployment(self, deployment_id):
            assert deployment_id == "d-Kappa"
            raise mekansm.errors.DeploymentNotFound(deployment_id)

    app = TestApp(handlers.wsgi_app(StubCtx()))
    resp = app.get("/deployments/d-Kappa", expect_errors=True)
    assert resp.status_int == 404


def test_deployment():
    fixture = get_sample_deployment_data()

    class StubCtx:
        def get_deployment(self, deployment_id):
            assert deployment_id == "d-1"
            return fixture

    app = get_test_app(StubCtx())
    resp = app.get("/deployments/d-1")
    assert resp.status_int == 200
    deployment_data = resp.json["data"]
    assert deployment_data["id"] == "d-1"
    fixture.pop("id")
    assert deployment_data["attributes"] == fixture
    assert deployment_data["type"] == "deployments"


def test_deployment_nodes_statuses():

    def _event_data():
        return [
            {
                "endTime": "2016-03-01T10:37:39.869000-08:00",
                "status": "Succeeded",
                "diagnostics": {
                    "errorCode": "Success",
                    "scriptName": "",
                    "logTail": "",
                    "message": "Succeeded"
                },
                "startTime": "2016-03-01T10:37:39.754000-08:00",
                "lifecycleEventName": "ApplicationStop"
            },
            {
                "endTime": "2016-03-01T10:37:41.329000-08:00",
                "status": "Succeeded",
                "diagnostics": {
                    "errorCode": "Success",
                    "scriptName": "",
                    "logTail": "",
                    "message": "Succeeded"
                },
                "startTime": "2016-03-01T10:37:40.994000-08:00",
                "lifecycleEventName": "DownloadBundle"
            }
        ]

    class StubCtx:
        def get_deployment(self, deployment_id):
            assert deployment_id == "d-1"
            return get_sample_deployment_data()

        def get_nodes(self, *node_ids):
            # assert filter_obj == {"deployment": "d-1"}
            return [{"id": "i-1", "datacenter": "dc-1"}]

        def get_deployment_instance_status(self, deployment_id, node_id):
            assert deployment_id == "d-1"
            assert node_id == "i-1"
            return {
                "node": node_id,
                "deployment": deployment_id,
                "update_time": utc.datetime(2010, 3, 3, 16, 20, 10),
                "status": "Failed"
            }

        def get_deployment_nodes_status(self, deployment_id):
            return [
                {
                    "node": "i-1",
                    "status": "Failed",
                    "lastUpdatedAt": "2010-03-03T16:20:10+00:00",
                    "lifecycleEvents": _event_data()
                }
            ]

    app = get_test_app(StubCtx())
    resp = app.get("/deployments/d-1/nodes_status")
    assert resp.status_int == 200
    assert len(resp.json["data"]) == 1
    data = resp.json["data"][0]
    assert data["id"] == {"node": "i-1", "deployment": "d-1"}
    assert data["attributes"] == {
        "status": "Failed",
        "update_time": "2010-03-03T16:20:10+00:00",
        "events": _event_data()
    }
    assert data["type"] == "node_status"


def test_deployment_node_status():

    class StubCtx:
        def get_deployment_instance_status(self, deployment_id, node_id):
            assert deployment_id == "d-1"
            assert node_id == "i-1"
            return {
                "node": node_id,
                "deployment": deployment_id,
                "update_time": utc.datetime(2010, 3, 3, 16, 20, 10),
                "status": "Failed"
            }

    app = get_test_app(StubCtx())
    resp = app.get("/deployments/d-1/nodes_status/i-1")
    assert resp.status_int == 200
    data = resp.json["data"]
    assert data["id"] == {"node": "i-1", "deployment": "d-1"}
    assert data["attributes"] == {
        "status": "Failed", "update_time": "2010-03-03T16:20:10+00:00"}
    assert data["type"] == "node_status"
