#
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
#

from typing import Any, List, Mapping, Tuple

import requests.exceptions
from airbyte_cdk.models import SyncMode
from airbyte_cdk.sources import AbstractSource
from airbyte_cdk.sources.streams import Stream
from airbyte_cdk.sources.streams.http.requests_native_auth import TokenAuthenticator

from .streams import (
    AccessCheck,
    Campaigns,
    CampaignsMetrics,
    Channels,
    CustomEvent,
    EmailBounce,
    EmailClick,
    EmailComplaint,
    EmailOpen,
    EmailSend,
    EmailSendSkip,
    EmailSubscribe,
    EmailUnsubscribe,
    Events,
    HostedUnsubscribeClick,
    InAppClick,
    InAppClose,
    InAppDelete,
    InAppDelivery,
    InAppOpen,
    InAppSend,
    InAppSendSkip,
    InboxMessageImpression,
    InboxSession,
    Lists,
    ListUsers,
    MessageTypes,
    Metadata,
    Purchase,
    PushBounce,
    PushOpen,
    PushSend,
    PushSendSkip,
    PushUninstall,
    SmsBounce,
    SmsClick,
    SmsReceived,
    SmsSend,
    SmsSendSkip,
    SmsUsageInfo,
    Templates,
    Users,
    WebPushClick,
    WebPushSend,
    WebPushSendSkip,
)


class SourceIterable(AbstractSource):
    """
    Note: there are some redundant endpoints
    (e.g. [`export/userEvents`](https://api.iterable.com/api/docs#export_exportUserEvents)
    and [`events/{email}`](https://api.iterable.com/api/docs#events_User_events)).
    In this case it's better to use the one which takes params as a query param rather than as part of the url param.
    """

    def check_connection(self, logger, config) -> Tuple[bool, any]:
        try:
            authenticator = TokenAuthenticator(token=config["api_key"], auth_header="Api-Key", auth_method="")
            list_gen = Lists(authenticator=authenticator).read_records(sync_mode=SyncMode.full_refresh)
            next(list_gen)
            return True, None
        except Exception as e:
            return False, f"Unable to connect to Iterable API with the provided credentials - {e}"

    def streams(self, config: Mapping[str, Any]) -> List[Stream]:
        def all_streams_accessible():
            access_check_stream = AccessCheck(authenticator=authenticator)
            slice_ = next(iter(access_check_stream.stream_slices(sync_mode=SyncMode.full_refresh)))
            try:
                list(access_check_stream.read_records(stream_slice=slice_, sync_mode=SyncMode.full_refresh))
            except requests.exceptions.RequestException as e:
                if e.response.status_code == requests.codes.UNAUTHORIZED:
                    return False
                raise
            else:
                return True

        authenticator = TokenAuthenticator(token=config["api_key"], auth_header="Api-Key", auth_method="")
        # end date is provided for integration tests only
        start_date, end_date = config["start_date"], config.get("end_date")
        date_range = {"start_date": start_date, "end_date": end_date}
        streams = [
            Campaigns(authenticator=authenticator),
            CampaignsMetrics(authenticator=authenticator, **date_range),
            Channels(authenticator=authenticator),
            Lists(authenticator=authenticator),
            MessageTypes(authenticator=authenticator),
            Metadata(authenticator=authenticator),
            Templates(authenticator=authenticator, **date_range),
        ]
        # Iterable supports two types of Server-side api keys:
        # - read only
        # - server side
        # The first one has a limited set of supported APIs, so others are filtered out here.
        # A simple check is done - a read operation on a stream that can be accessed only via a Server side API key.
        # If read is successful - other streams should be supported as well.
        # More on this - https://support.iterable.com/hc/en-us/articles/360043464871-API-Keys-
        if all_streams_accessible():
            streams.extend(
                [
                    Users(authenticator=authenticator, **date_range),
                    ListUsers(authenticator=authenticator),
                    EmailBounce(authenticator=authenticator, **date_range),
                    EmailClick(authenticator=authenticator, **date_range),
                    EmailComplaint(authenticator=authenticator, **date_range),
                    EmailOpen(authenticator=authenticator, **date_range),
                    EmailSend(authenticator=authenticator, **date_range),
                    EmailSendSkip(authenticator=authenticator, **date_range),
                    EmailSubscribe(authenticator=authenticator, **date_range),
                    EmailUnsubscribe(authenticator=authenticator, **date_range),
                    PushSend(authenticator=authenticator, **date_range),
                    PushSendSkip(authenticator=authenticator, **date_range),
                    PushOpen(authenticator=authenticator, **date_range),
                    PushUninstall(authenticator=authenticator, **date_range),
                    PushBounce(authenticator=authenticator, **date_range),
                    WebPushSend(authenticator=authenticator, **date_range),
                    WebPushClick(authenticator=authenticator, **date_range),
                    WebPushSendSkip(authenticator=authenticator, **date_range),
                    InAppSend(authenticator=authenticator, **date_range),
                    InAppOpen(authenticator=authenticator, **date_range),
                    InAppClick(authenticator=authenticator, **date_range),
                    InAppClose(authenticator=authenticator, **date_range),
                    InAppDelete(authenticator=authenticator, **date_range),
                    InAppDelivery(authenticator=authenticator, **date_range),
                    InAppSendSkip(authenticator=authenticator, **date_range),
                    InboxSession(authenticator=authenticator, **date_range),
                    InboxMessageImpression(authenticator=authenticator, **date_range),
                    SmsSend(authenticator=authenticator, **date_range),
                    SmsBounce(authenticator=authenticator, **date_range),
                    SmsClick(authenticator=authenticator, **date_range),
                    SmsReceived(authenticator=authenticator, **date_range),
                    SmsSendSkip(authenticator=authenticator, **date_range),
                    SmsUsageInfo(authenticator=authenticator, **date_range),
                    Purchase(authenticator=authenticator, **date_range),
                    CustomEvent(authenticator=authenticator, **date_range),
                    HostedUnsubscribeClick(authenticator=authenticator, **date_range),
                    Events(authenticator=authenticator),
                ]
            )
        return streams
