import pytest
import json

from poe_new_relic.api import NewRelic, NEWRELIC_GRAPHQL_URL
from test_utils.mock import MockApi
from typing import Tuple


class TestNewRelic_UnitTests:
    @pytest.mark.unit
    def test_nerdgraph_nrql_search(self):
        # Set expected values
        expected_api_key = "derp"
        expected_account_id = 12345
        expected_nrql = "SELECT uniques(appName) FROM Transactions SINCE 1 hour ago"
        expected_label = "apmAppNames"
        expected_timeout_ms = 123456
        expected_results = {expected_label: {"results": ["Example App 1", "Example App 2"]}}
        expected_resp = {"data": {"actor": {"account": expected_results}}}

        # Set up mocks
        mock_response = MockApi()
        mock_response.on("raise_for_status").return_val(None)
        mock_response.on("json").return_val(expected_resp)

        mock_http = MockApi()
        mock_http.on(
            "post",
            url=NEWRELIC_GRAPHQL_URL,
            headers={"API-Key": expected_api_key},
            json={
                "query": f'{{actor {{account(id: {expected_account_id}) {{apmAppNames: nrql(query: "{expected_nrql}") {{results}}}}}}}}',
                "timeout": expected_timeout_ms,
            },
        ).return_val(mock_response)

        # Test nerdgraph_nrql_search()
        nr = NewRelic(expected_api_key, http_session=mock_http)
        results = nr.nerdgraph_nrql_search(
            account_id=expected_account_id,
            nrql=expected_nrql,
            label=expected_label,
            timeout_ms=expected_timeout_ms,
        )
        assert results == expected_results
        mock_http.assert_expectations()
        mock_response.assert_expectations()

    @pytest.mark.unit
    def test_nerdgraph_entitysearch(self):
        # Set expected values
        expected_api_key = "derp"
        expected_query = "sql"
        expected_output_keys = "{keys}"
        expected_on = "... on SyntheticMonitorEntityOutline"
        expected_return_list = [
            {"name": "derp Highered", "tags": [{"key": "wallBoardProducts", "values": ["durp"]}]}
        ]

        # Set up mocks
        mock_http, mock_response = self.get_mocks(expected_query, expected_output_keys, expected_on)

        # Test nerdgraph_entitysearch()
        nr = NewRelic(expected_api_key, http_session=mock_http)
        resp = nr.nerdgraph_entitysearch(expected_query, expected_output_keys, on=expected_on)
        assert resp == expected_return_list
        mock_http.assert_expectations()
        mock_response.assert_expectations()

    @pytest.mark.unit
    def test_nerdgraph_entitysearch_exception(self):
        # Set expected values
        expected_api_key = "derp"
        expected_query = "sql"
        expected_output_keys = "{keys}"
        expected_on = "... on SyntheticMonitorEntityOutline"
        expected_exception = "test error"

        # Set up mocks
        mock_http, mock_response = self.get_mocks(
            expected_query, expected_output_keys, expected_on, is_error=1
        )

        # Test nerdgraph_entitysearch()
        nr = NewRelic(expected_api_key, http_session=mock_http)
        with pytest.raises(Exception) as e_info:
            nr.nerdgraph_entitysearch(expected_query, expected_output_keys, on=expected_on)
        assert str(e_info.value) == expected_exception
        mock_http.assert_expectations()
        mock_response.assert_expectations()

    @pytest.mark.unit
    def test_get_synths(self):
        # Set expected values
        expected_api_key = "derp"
        expected_query = (
            'entitySearch(query: "domain = \u0027SYNTH\u0027 AND type = \u0027MONITOR\u0027")'
        )
        expected_output_keys = "{guid name tags {key values} monitorId}"
        expected_on = "... on SyntheticMonitorEntityOutline"
        expected_return_list = [
            {"name": "derp Highered", "tags": [{"key": "wallBoardProducts", "values": ["durp"]}]}
        ]

        # Set up mocks
        mock_http, mock_response = self.get_mocks(expected_query, expected_output_keys, expected_on)

        # Test get_synths()
        nr = NewRelic(expected_api_key, http_session=mock_http)
        resp = nr.get_synths()
        assert resp == expected_return_list
        mock_http.assert_expectations()
        mock_response.assert_expectations()

    @pytest.mark.unit
    def test_get_monitorid_and_guids_synths_by_tag(self):
        # Set expected values
        expected_tag = "wallBoardProducts"
        expected_api_key = "derp"
        expected_query = (
            'entitySearch(query: "domain = \u0027SYNTH\u0027 AND type = \u0027MONITOR\u0027")'
        )
        expected_output_keys = "{guid name tags {key values} monitorId}"
        expected_return_list = {"durp": [{"guid": [None]}, {"monitorId": [None]}]}
        expected_on = "... on SyntheticMonitorEntityOutline"

        # Set up mocks
        mock_http, mock_response = self.get_mocks(expected_query, expected_output_keys, expected_on)

        # Test get_monitorid_and_guids_synths_by_tag()
        nr = NewRelic(expected_api_key, http_session=mock_http)
        resp = nr.get_monitorid_and_guids_synths_by_tag(expected_tag)
        # the order of the keys can be flip flopped randomly, this covers that
        assert resp == expected_return_list or {"durp": [{"monitorId": [None]}, {"guid": [None]}]}
        mock_http.assert_expectations()
        mock_response.assert_expectations()

    @pytest.mark.unit
    def test_get_scorecard_performance_dashboards(self):
        # Set expected values
        expected_tag = "wallBoardProducts"
        expected_api_key = "derp"
        expected_query = 'entitySearch(query: "((name LIKE \u0027Scorecard -%\u0027) OR (name LIKE \u0027Performance -%\u0027)) AND (domain = \u0027VIZ\u0027 AND type = \u0027DASHBOARD\u0027)")'
        expected_output_keys = "{guid name updatedAt accountId permalink createdAt}"
        expected_return_list = [
            {"name": "derp Highered", "tags": [{"key": "wallBoardProducts", "values": ["durp"]}]}
        ]
        expected_on = "... on DashboardEntityOutline"

        # Set up mocks
        mock_http, mock_response = self.get_mocks(expected_query, expected_output_keys, expected_on)

        # Test get_scorecard_performance_dashboards()
        nr = NewRelic(expected_api_key, http_session=mock_http)
        resp = nr.get_scorecard_performance_dashboards()
        assert resp == expected_return_list
        mock_http.assert_expectations()
        mock_response.assert_expectations()

    def get_mocks(
        self, query: str, output_keys: str, on: str = "", is_error: bool = 0
    ) -> Tuple[MockApi, MockApi]:
        expected_api_key = "derp"
        expected_next_cursor = ""
        expected_return_list = [
            {"name": "derp Highered", "tags": [{"key": "wallBoardProducts", "values": ["durp"]}]}
        ]
        expected_full_query = {
            "query": f"{{actor {{{query} {{results{expected_next_cursor} {{entities {{{on} {output_keys}}}nextCursor}}}}}}}}"
        }
        expected_resp_json = {
            "data": {"actor": {"entitySearch": {"results": {"entities": expected_return_list}}}},
            "nextCursor": None,
        }
        if is_error:
            expected_resp_json["errors"] = [{"message": "test error"}]
        mock_request = MockApi()
        setattr(mock_request, "url", NEWRELIC_GRAPHQL_URL)
        setattr(mock_request, "method", "POST")
        setattr(mock_request, "headers", {})
        mock_resp = MockApi()
        setattr(mock_resp, "request", mock_request)
        mock_resp.on("raise_for_status").return_val(None)
        mock_resp.on("json").return_val(expected_resp_json)
        mock_http_session = MockApi()
        mock_http_session.on(
            "post",
            url=NEWRELIC_GRAPHQL_URL,
            headers={"API-Key": expected_api_key},
            json=expected_full_query,
        ).return_val(mock_resp)
        return mock_http_session, mock_resp
