# pylint: disable=no-self-use,unused-import,too-few-public-methods,raise-missing-from,broad-except,too-many-instance-attributes
""""
MODULE FOR ONESIGNAL API CALL
"""
import json
import time
from datetime import datetime
from urllib.error import URLError

import pandas as pd
import requests
from dateutil import parser

from sdc_dp_helpers.api_utilities.date_managers import date_handler
from sdc_dp_helpers.api_utilities.file_managers import load_file
from sdc_dp_helpers.api_utilities.retry_managers import retry_handler, request_handler


class CustomOneSignalReader:
    """ "One Signal Custom Reader class"""

    def __init__(self, creds_file, config_file, **kwargs):
        self._creds = load_file(creds_file, "yml")
        self._config = load_file(config_file, "yml")

        self._header = {
            "Content-Type": "application/json; charset=utf-8",
            "Authorization": f"Basic {self._creds.get('api_key')}",
            "User-Agent": "Mozilla/5.0",
        }

        self._request_session = requests.Session()
        self.offset, self.data_set = 0, []

        self._csv_wait_time = kwargs.get("csv_wait_time", 90)
        self._csv_get_attempts = kwargs.get("csv_get_attempts", 10)

    def _generate_url(self, endpoint: str):
        """
        Generate url string for authentication from given endpoints.
        :param endpoint: view_notification or csv_export
        :return: Url string.
        """
        _app_id = self._creds.get("app_id")
        if endpoint == "view_notifications":
            return f"https://onesignal.com/api/v1/notifications?app_id={_app_id}"
        if endpoint == "csv_export":
            return f"https://onesignal.com/api/v1/players/csv_export?app_id={_app_id}"

        raise ValueError(
            f"Given endpoint: {endpoint} is not supported. "
            f"Try csv_export or view_notifications."
        )

    @request_handler(wait=0.5, backoff_factor=0.5)
    def _view_notification_query_handler(self, limit: int, offset: int):
        """
        Handles the view notification request attempt.
        """
        url = self._generate_url(endpoint="view_notifications")
        return requests.get(
            url=url,
            headers=self._header,
            params={"limit": limit, "total_count": "true", "offset": offset},
            timeout=30,
        )

    @request_handler(wait=1, backoff_factor=0.5)
    def _csv_export_query_handler(self):
        """
        Handles the csv export request attempt.
        """
        url = self._generate_url(endpoint="csv_export")
        return requests.post(url=url, headers=self._header, timeout=30)

    def _get_csv_export_handler(self, response):
        """
        Attempts to gather the data from the csv url, and waits while
        it is being generated.
        :param response: Onesignal response object.
        """
        csv_url = response.get("csv_file_url", None)
        if csv_url is not None:
            attempts = 1
            while True:
                try:
                    print(f"Attempting to gather data from url: {csv_url}.")
                    data_frame: pd.DataFrame = pd.read_csv(csv_url)
                    self.data_set = data_frame.to_dict(orient="records")

                    # create a usable field to write to s3
                    # also handle timestamps that have milliseconds, it's not
                    # always there, so we need to handle both
                    for row in self.data_set:
                        try:
                            row["created_at_date"] = str(
                                datetime.strptime(
                                    row["created_at"], "%Y-%m-%d %H:%M:%S"
                                ).date()
                            ).replace("-", "")
                        except ValueError:
                            row["created_at_date"] = str(
                                datetime.strptime(
                                    row["created_at"], "%Y-%m-%d %H:%M:%S.%f"
                                ).date()
                            ).replace("-", "")

                    print("Data gathered...")
                    return self.data_set

                except URLError:
                    print(
                        f"CSV file not generated, waiting for {self._csv_wait_time} seconds. "
                        f"Attempt {attempts}/{self._csv_get_attempts}."
                    )
                    time.sleep(self._csv_wait_time)
                    if attempts >= self._csv_get_attempts:
                        raise ConnectionError(
                            f"CSV file failed to generate after {attempts} attempts. "
                            "Contact Onesignal for help."
                        )
                    attempts += 1
        else:
            raise ConnectionError(response)

    def run_query(self):
        """
        Generate a compressed CSV export of all of your current user data.
        This method can be used to generate a compressed CSV export of current user data.
        View the details of multiple notifications.
        :return: The response dataset.
        """
        _endpoint = self._config.get("endpoint")
        _limit = self._config.get("limit", 50)
        print(f"Gathering data for {_endpoint}.")

        if _endpoint == "view_notifications":
            # gather data per offset given the set of limits
            initial_response = self._view_notification_query_handler(
                limit=_limit, offset=0
            ).json()
            _total_records = int(initial_response.get("total_count"))

            for offset in range(0, _total_records, _limit):
                print(f"At offset {offset} of {_total_records}.")
                response = self._view_notification_query_handler(
                    limit=_limit, offset=offset
                ).json()
                for notification in response.get("notifications"):
                    self.data_set.append(notification)

            return self.data_set

        if _endpoint == "csv_export":
            # if connection is good, try to get the csv file from the Onesignal
            # Athena wrapper, it could take time to generate, so wait for it.
            response = self._csv_export_query_handler().json()
            self._get_csv_export_handler(response=response)
            return self.data_set

        raise ValueError(
            f"Given endpoint: {_endpoint} is not supported. "
            f"Try csv_export or view_notifications."
        )
