# Copyright 2022-2025 ScaleVector
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from enum import Enum
from typing import Any, Dict, Iterator, Optional, Tuple

from dlt.common.typing import DictStrStr, TDataItems, TSecretValue
from dlt.sources.helpers.requests import client

from .. import settings
from .credentials import (
    TZendeskCredentials,
    ZendeskCredentialsEmailPass,
    ZendeskCredentialsOAuth,
    ZendeskCredentialsToken,
)


class PaginationType(Enum):
    OFFSET = 0
    CURSOR = 1
    STREAM = 2
    START_TIME = 3


class ZendeskAPIClient:
    """
    API client used to make requests to Zendesk talk, support and chat API
    """

    subdomain: str = ""
    url: str = ""
    headers: Optional[DictStrStr]
    auth: Optional[Tuple[str, TSecretValue]]

    def __init__(
        self, credentials: TZendeskCredentials, url_prefix: Optional[str] = None
    ) -> None:
        """
        Initializer for the API client which is then used to make API calls to the ZendeskAPI

        Args:
            credentials: ZendeskCredentials object which contains the necessary credentials to authenticate to ZendeskAPI
        """
        # oauth token is the preferred way to authenticate, followed by api token and then email + password combo
        # fill headers and auth for every possibility of credentials given, raise error if credentials are of incorrect type
        if isinstance(credentials, ZendeskCredentialsOAuth):
            self.headers = {"Authorization": f"Bearer {credentials.oauth_token}"}
            self.auth = None
        elif isinstance(credentials, ZendeskCredentialsToken):
            self.headers = None
            self.auth = (f"{credentials.email}/token", credentials.token)
        elif isinstance(credentials, ZendeskCredentialsEmailPass):
            self.auth = (credentials.email, credentials.password)
            self.headers = None
        else:
            raise TypeError(
                "Wrong credentials type provided to ZendeskAPIClient. The credentials need to be of type: ZendeskCredentialsOAuth, ZendeskCredentialsToken or ZendeskCredentialsEmailPass"
            )

        # If url_prefix is set it overrides the default API URL (e.g. chat api uses zopim.com domain)
        if url_prefix:
            self.url = url_prefix
        else:
            self.subdomain = credentials.subdomain
            self.url = f"https://{self.subdomain}.zendesk.com"

    def get_pages(
        self,
        endpoint: str,
        data_point_name: str,
        pagination: PaginationType,
        params: Optional[Dict[str, Any]] = None,
    ) -> Iterator[TDataItems]:
        """
        Makes a request to a paginated endpoint and returns a generator of data items per page.

        Args:
            endpoint: The url to the endpoint, e.g. /api/v2/calls
            data_point_name: The key which data items are nested under in the response object (e.g. calls)
            params: Optional dict of query params to include in the request
            pagination: Type of pagination type used by endpoint

        Returns:
            Generator of pages, each page is a list of dict data items
        """
        # update the page size to enable cursor pagination
        params = params or {}
        if pagination == PaginationType.CURSOR:
            params["page[size]"] = settings.PAGE_SIZE
        elif pagination == PaginationType.STREAM:
            params["per_page"] = settings.INCREMENTAL_PAGE_SIZE
        elif pagination == PaginationType.START_TIME:
            params["limit"] = settings.INCREMENTAL_PAGE_SIZE

        # make request and keep looping until there is no next page
        get_url = f"{self.url}{endpoint}"
        while get_url:
            response = client.get(
                get_url, headers=self.headers, auth=self.auth, params=params
            )
            response.raise_for_status()
            response_json = response.json()
            result = response_json[data_point_name]
            yield result

            get_url = None
            if pagination == PaginationType.CURSOR:
                if response_json["meta"]["has_more"]:
                    get_url = response_json["links"]["next"]
            elif pagination == PaginationType.OFFSET:
                get_url = response_json.get("next_page", None)
            elif pagination == PaginationType.STREAM:
                # See https://developer.zendesk.com/api-reference/ticketing/ticket-management/incremental_exports/#json-format
                if not response_json["end_of_stream"]:
                    get_url = response_json["next_page"]
            elif pagination == PaginationType.START_TIME:
                if response_json["count"] > 0:
                    get_url = response_json["next_page"]

            params = {}
