import requests
import json
import logging
import os

from requests.models import Response
from poe_new_relic import get_logger
from typing import Any, Iterator, List, Union, Dict

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

class NewRelic:
    def __init__(self, api_key: str, http_session: requests.Session = requests.Session(), logger: logging.Logger = None):
        self._http_session = http_session
        self.__logger = logger or get_logger()
        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
        """
        more, entities, next_cursor= True, [], ''
        while more:
            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(NERDGRAPH_URL, 
                                                   headers=self._headers, 
                                                   data=json.dumps(full_query))
                response.raise_for_status()
                resp_dict = response.json()
                results = resp_dict['data']['actor']['entitySearch']['results']
                entities.extend(results.get('entities'))
                self.__logger.info(f'Total entities: {len(entities)}')    
                next_cursor = results.get('nextCursor')
                more = next_cursor
            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 = \u0027SYNTH\u0027 AND type = \u0027MONITOR\u0027\")'
        on = '... on SyntheticMonitorEntityOutline'
        output_keys = '{\n            guid\n            name\n            tags {\n              key\n              values\n            }\n            monitorId\n          }' 
        resp = self.nerdgraph_entitysearch(entity_search, output_keys, on)
        print(len(resp))
        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 \u0027Scorecard -%\u0027) OR (name LIKE \u0027Performance -%\u0027)) AND (domain = \u0027VIZ\u0027 AND type = \u0027DASHBOARD\u0027)\")'
        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


if __name__ == '__main__':

    nr = NewRelic("")
    results = nr.get_scorecard_performance_dashboards()
    # results = nr.get_monitorid_and_guids_synths_by_tag('wallBoardProduct')
    # print(results)
    print(len(results))
    
