import requests
import json
import logging
import os

from poe_new_relic import get_logger, log_it
from typing import List, Dict

NEWRELIC_GRAPHQL_URL = os.environ.get("NERDGRAPH_URL", "https://api.newrelic.com/graphql")


class NewRelic:
    @log_it
    def __init__(
        self,
        api_key: str,
        base_url: str = None,
        http_session: requests.Session = requests.Session(),
        logger: logging.Logger = None,
    ):
        self._http_session = http_session
        self.__logger = logger or get_logger()
        self._base_url = base_url or NEWRELIC_GRAPHQL_URL
        self._headers = {
            "Accept": "application/json",
            "API-Key": api_key,
            "Content-Type": "application/json",
        }

    def nerdgraph_entitysearch(self, query: str, output_keys: str, on: str = "") -> Dict:
        """_summary_
            query New Relic's Nerdgraph api, https://api.newrelic.com/graphiql
        Args:
            query (str): the SELECT section of the NRQL query
            on (str): appears to be a sub filtering of what to return and contains proprietary return values for what you pick
            output_keys (str): the keys to return values for

        Returns:
            Dict: dynamic results defined by the output_keys arg
        """
        entities, next_cursor = [], ""
        while True:
            try:
                if next_cursor:
                    next_cursor = f'(cursor: "{next_cursor}")'
                # TODO allow for multiple 'on' values to be used and built out in the query string like in the nerdgraph explorer
                full_query = {
                    "query": f"{{actor {{{query} {{results{next_cursor} {{entities {{{on} {output_keys}}}nextCursor}}}}}}}}",
                    "variables": "",
                }
                response = self._http_session.post(
                    self._base_url, headers=self._headers, data=json.dumps(full_query)
                )
                response.raise_for_status()
                resp_json = response.json() or {}
                if resp_json.get("errors"):
                    err_messages = [
                        err["message"] for err in resp_json["errors"] if err.get("message")
                    ]
                    raise Exception("\n".join(err_messages))
                results = resp_json["data"]["actor"]["entitySearch"]["results"]
                entities.extend(results.get("entities"))
                self.__logger.info(f"Total entities: {len(entities)}")
                next_cursor = results.get("nextCursor", None)
                if not next_cursor:
                    self.__logger.info(f"Completed Cursor")
                    break
            except Exception as e:
                self.__logger.error(f"Failed to start New Relic Nerdgraph query execution: {e}")
                raise e
        return entities

    def get_synths(self) -> List:
        """_summary_
            cursors over Nerdgraph's entitySearch query results to return every synthetic
        Raises:
            e: logs the response back from new relic regarding the query attempt and raises the actual error
        Returns:
            List: the combined cursored results of all of the synthetics
        """
        entity_search = "entitySearch(query: \"domain = 'SYNTH' AND type = 'MONITOR'\")"
        on = "... on SyntheticMonitorEntityOutline"
        output_keys = "{guid name tags {key values} monitorId}"
        resp = self.nerdgraph_entitysearch(entity_search, output_keys, on)
        return resp

    def get_monitorid_and_guids_synths_by_tag(self, tag: str) -> Dict:
        """_summary_
            groups monitorIds and guids by tag in an array
        Args:
            tag (str): tag of an entity

        Returns:
            Dict: containing per tag grouping of an array of monitorIds and array of guids
        """
        wb_tagged = []
        all_synths = self.get_synths()
        # get only synthetics having said tag
        wb_tagged.extend(
            [
                i
                for e in all_synths
                for i in [",".join(t["values"]) for t in e.get("tags") if t["key"] == tag]
            ]
        )
        self.__logger.info(f"Found {len(wb_tagged)} entities with tag {tag}")
        # geting distinct set of tags
        set_tags = set(wb_tagged)
        # reducing the  keys down grabbing monitorIds and guids per tag
        panel_entities = [
            {t: {"monitorId": e.get("monitorId"), "guid": e.get("guid")}}
            for e in all_synths
            for t in set_tags
            for l in [w for w in e.get("tags") if t in w["values"]]
        ]
        # grouping tags to array of dicts, {tag:[{monitorId:value, guid:value}]}
        panel_entities = {
            k: [d.get(k) for d in panel_entities if k in d] for k in set().union(*panel_entities)
        }
        # creating arrays per monitorId and per guid per tag, {tag:[{monitorId:[values]},{guid:[values]}]} TODO change array of dicts for tags to single dict with the keys
        panel_entities = {
            g: [
                {
                    k: [t.get(k) for t in panel_entities.get(g) if k in t]
                    for x in panel_entities.get(g)
                }
                for k in set().union(*panel_entities.get(g))
            ]
            for g in set_tags
            if panel_entities.get(g)
        }
        return panel_entities

    def get_scorecard_performance_dashboards(self) -> List:
        """_summary_
            returns the raw array of entities of dashboards filtered on name like Performance and Scorecard
        Raises:
            e: logs the response back from new relic regarding the query attempt and raises the actual error

        Returns:
            List: array of the entities built out from the results of the cursor/paginator
        """
        entity_search = "entitySearch(query: \"((name LIKE 'Scorecard -%') OR (name LIKE 'Performance -%')) AND (domain = 'VIZ' AND type = 'DASHBOARD')\")"
        on = "... on DashboardEntityOutline"
        output_keys = "{guid name updatedAt accountId permalink createdAt}"
        resp = self.nerdgraph_entitysearch(entity_search, output_keys, on)
        main_dashboards = [
            x
            for x in resp
            if x
            for k, v in x.items()
            if k == "name" and v.split()[-1] == "Highered"
        ]
        self.__logger.info(
            f"Found {len(main_dashboards)} Scorecard and Performance dashboards found"
        )
        return main_dashboards
