import json
import logging
import requests

from pagerduty_api import DEFAULT_LOGGER, log_it
from typing import Any, Dict, List


class AbstractClient():
    def __init__(
        self,
        http_session: requests.Session = requests.Session(),
        endpoint: str = 'https://api.pagerduty.com',
        auth_token: str = None,
        logger: logging.Logger = None
    ):
        self._endpoint = endpoint
        self._http_session = http_session
        self._auth_token = auth_token
        self._valid_methods = ['get', 'patch', 'post', 'put', 'head', 'delete']
        self._logger = logger or DEFAULT_LOGGER

    @log_it
    def do_request(
        self,
        url: str,
        entity_name: str,
        headers: Dict[str, str] = {},
        params: Dict[str, Any] = {},
        paginate: bool = True,
        method: str = 'get',
        data: Any = None
    ) -> List[Any]:
        assert url, f'Cannot do HTTP request against url: {url}'
        assert entity_name, f'Cannot do HTTP request with entity: {entity_name}'
        assert method in self._valid_methods, f'"{method}" is not a valid request method'
        headers = self._set_default_headers(headers)
        params = self._set_default_params(params)
        pages = {
            'more': True,
            'limit': params['limit'],
            'offset': params['offset']
        }

        # FIXME: use a logger
        # print(f'Executing HTTP {method} on {url}')
        

        records = []
        while pages.get('more'):  # we always make at least one call...
            response = getattr(self._http_session, method)(
                url,
                headers=headers,
                params=params,
                data=data
            )

            
            # self._logger.info(response.request.headers)
            # self._logger.info(f'{response.request.method} {response.request.url}')
            if response.status_code in [204, 400, 404, 500]:  # return no content
                # FIXME: use a logger
                print(f'HTTP response returned with status {response.status_code}. Returning empty records.')
                return records

            pages = response.json()
            if 'offset' in pages:
                records.extend(pages[entity_name])
                params['offset'] = pages['offset'] + pages['limit']
            elif 'ok' not in pages:
                records.append(pages[entity_name])

            if not paginate or method != 'get':  # disable pagination
                break

        return records

    @log_it
    def do_analytics_request(
        self,
        url: str,
        entity_name: str,
        headers: Dict[str, str] = {},
        data: Dict[str, Any] = {},
        method: str = 'post'
    ) -> List[Any]:
        assert url, f'Cannot do HTTP request against url: {url}'
        assert entity_name, f'Cannot do HTTP request with entity: {entity_name}'
        assert method in self._valid_methods, f'"{method}" is not a valid request method'
        
        headers = {
            'x-early-access': "analytics-v2",
            'accept': "application/vnd.pagerduty+json;version=2",
            'content-type': "application/json",
            'authorization': f"Token token={self._auth_token}"
        }
        payload = self._set_default_analytics_data(data)
        response = requests.request("POST", url, data=json.dumps(payload), headers=headers)
        
        self._logger.debug(f'Headers {response.request.headers}')
        self._logger.info(f'executing {response.request.method} on {response.request.url} with data {response.request.body}')
        if response.status_code in [204, 400, 404, 500]:  # return no content
            # FIXME: use a logger
            self._logger.error(f'HTTP response returned with status {response.status_code}. Returning empty records.')
            return []

        resp = []
        if response:
            resp = response.json().get(entity_name,[])
        return resp

    @log_it
    def _set_default_headers(self, headers: Dict[str, str]) -> Dict[str, str]:
        '''Set the default Accept, Authorization, and Content-type headers if not already present'''
        cp = {} if headers is None else headers.copy()
        cp['Accept'] = cp.get('Accept', 'application/vnd.pagerduty+json;version=2')
        cp['Authorization'] = cp.get('Authorization', f'Token token={self._auth_token}')
        cp['Content-type'] = cp.get('Content-type', 'application/json')
        return cp

    @log_it
    def _set_default_params(self, params: Dict[str, Any]) -> Dict[str, Any]:
        '''Set the default limit and offset params if not already present'''
        cp = {} if params is None else params.copy()
        cp['limit'] = cp.get('limit', 100)
        cp['offset'] = cp.get('offset', 0)
        return cp

    @log_it
    def _set_default_analytics_data(self, data: Dict[str, Any], aggregate_unit: str = 'day', urgency: str = 'high') -> Dict[str, Any]:
        '''Set the default limit and offset params if not already present'''
        if urgency:
            data['urgency'] = urgency
        cp = {
            'filters': data    
        }
        if aggregate_unit:
            cp['aggregate_unit'] = aggregate_unit
        self._logger.info(cp)
        return cp

    @log_it
    def get_entity_tags(
        self,
        url: str,
        entity_id: str,
        params: Dict[str, Any] = None
    ):
        assert entity_id, f'Cannot get entity tags with entity id: {entity_id}'
        url = url if url else f'{self._endpoint}/{self._default_entity_name}'
        resp = self.do_request(
            url=f'{url}/{entity_id}/tags',
            entity_name='tags',
            params=params
        )

        tag = None
        if len(resp) == 1:
            tag = resp[0]
        return tag

    @log_it
    def modify_entity_tags(
        self,
        url: str,
        entity_id: str,
        assignments: Dict[Any, Any] = None
    ):
        assert url, f'Cannot modify entity tags with url: {url}'
        assert entity_id, f'Cannot modify entity tags with entity id: {entity_id}'
        return self.do_request(
            url=f'{url}/{entity_id}/change_tags',
            entity_name='tag',
            method='post',
            data=json.dumps(assignments)
        )
