import AnalyticsClient
import ComposableArchitecture

extension RootCoordinatorV1 {
    struct Analytics: AnalyticsReducer {
        var source: String = "root_coordinator"

        // swiftlint:disable:next cyclomatic_complexity function_body_length
        func analytics(before: State, after _: State, action: Action) -> Effect<Action> {
            switch action {
            case .createTapped:
                track(
                    Event(
                        category: .create,
                        actionName: .createTapped,
                        actionType: .tap,
                        elementType: .button,
                        method: "no_compact_player"
                    )
                )

            case .handleDeeplink(let deeplinkIntent):
                track(
                    Event(
                        category: .general,
                        actionName: .handleDeepLinkUrl,
                        elementType: .url,
                        elementId: String(describing: deeplinkIntent)
                    )
                )

            case .rootTab(.delegate(.showGeneratedClips)):
                track(
                    Event(
                        category: .general,
                        actionName: .showGeneratedClipsTapped,
                        actionType: .tap,
                        elementType: .toast,
                        // `elementId` over `createdId` as this is a tap gesture pointing to multiple clip ids, not the creation event itself
                        elementId: before.recentGeneratedClips?.map { $0.id.remoteId }.toString()
                    )
                )

            /// Called to show an alert upon login or session refresh
            case .showBrandedAlert(let brandedAlertStyle):
                let context: String = switch brandedAlertStyle {
                case .noAlert:
                    "no_alert"
                case .announceAlert(let announceAlertStyle):
                    announceAlertStyle.textCopy.toString()
                case .singleButtonAlert(let singleButtonAlertStyle):
                    singleButtonAlertStyle.textCopy.toString()
                case .multiButtonAlert(let multiButtonAlertStyle):
                    multiButtonAlertStyle.toString()
                case .listAlert(let listAlertStyle):
                    listAlertStyle.textCopy.toString()
                case .featuredArtist(let featuredArtistStyle):
                    featuredArtistStyle.textCopy.toString()
                case .versioningAlert(let versioningAlertStyle):
                    versioningAlertStyle.textCopy.toString()
                case .discountAlert(let discountAlertStyle):
                    discountAlertStyle.analyticsAttribute.toString()
                case .walkthroughAlert(let walkthroughAlertStyle):
                    walkthroughAlertStyle.textCopy.toString()
                case .hookStatusAlert(let hookStatusAlertStyle):
                    hookStatusAlertStyle.textCopy.toString()
                }
                track(
                    Event(
                        category: .general,
                        actionName: .showBrandedAlert,
                        elementType: .alert,
                        context: context
                    )
                )

            case .pauseAndDismissPlayer:
                track(
                    Event(
                        category: .general,
                        actionName: .pauseAndDismissPlayer
                    )
                )

            // The `.success` version of this response fires off periodically and isn't particularly useful to track
            case .internal(.checkCompletionResponse(.failure(let error))):
                track(
                    Event(
                        category: .create,
                        actionName: .clipCreationFailed,
                        context: error.underlyingError
                    )
                )

            case .internal(.generationComplete(let clips)):
                track(
                    Event(
                        category: .create,
                        actionName: .clipGenerationComplete,
                        elementType: .clip,
                        // See `case .internal(.checkCompletionResponse(let result)):` above
                        createdId: clips.map { $0.id.remoteId }.toString()
                    )
                )

            case .internal(.serviceStatusResponse(let result)):
                switch result {
                case .success(let serviceStatus):
                    track(
                        Event(
                            category: .general,
                            actionName: .serviceStatusFetchSucceeded,
                            context: serviceStatus?.toString()
                        )
                    )

                case .failure(let error):
                    track(
                        Event(
                            category: .general,
                            actionName: .serviceStatusFetchFailed,
                            context: error.underlyingError
                        )
                    )
                }

            case .internal(.billingInfoResponse(let result)):
                switch result {
                case .success(let billingInfo):
                    track(
                        Event(
                            category: .general,
                            actionName: .billingInfoFetchSucceeded,
                            context: billingInfo.toString()
                        )
                    )

                case .failure(let error):
                    track(
                        Event(
                            category: .general,
                            actionName: .billingInfoFetchFailed,
                            context: error.underlyingError
                        )
                    )
                }

            case .internal(.playNewClips):
                track(
                    Event(
                        category: .audioPlayer,
                        actionName: .playNewClips,
                        elementType: .clip,
                        elementId: before.recentGeneratedClips?.map { $0.id.remoteId }.toString()
                    )
                )

            case .deeplink(.clipResponse(let result)):
                switch result {
                case .success(let clip):
                    track(
                        Event(
                            category: .general,
                            actionName: .deepLinkClipFetchSucceeded,
                            elementType: .clip,
                            elementId: clip.id.remoteId
                        )
                    )

                case .failure(let error):
                    track(
                        Event(
                            category: .general,
                            actionName: .deepLinkClipFetchFailed,
                            elementType: .clip,
                            context: error.underlyingError
                        )
                    )
                }

            case .deeplink(.playlistResponse(let result)):
                switch result {
                case .success(let playlist):
                    track(
                        Event(
                            category: .general,
                            actionName: .deepLinkPlaylistFetchSucceeded,
                            elementType: .playlist,
                            elementId: playlist.id
                        )
                    )

                case .failure(let error):
                    track(
                        Event(
                            category: .general,
                            actionName: .deepLinkPlaylistFetchFailed,
                            elementType: .playlist,
                            context: error.underlyingError
                        )
                    )
                }
            case .deeplink(.hookResponse(_, let result)):
                switch result {
                case .success(let hook):
                    track(
                        Event(
                            category: .general,
                            actionName: .deepLinkHookFetchSucceeded,
                            elementType: .hook,
                            elementId: hook.id
                        )
                    )

                case .failure(let error):
                    track(
                        Event(
                            category: .general,
                            actionName: .deepLinkHookFetchFailed,
                            elementType: .hook,
                            context: error.underlyingError
                        )
                    )
                }

            default:
                break
            }
            return .none
        }
    }
}

extension CreditsPopup {
    struct Analytics: AnalyticsReducer {
        var source: String = "credits_popup"

        func analytics(before: State, after _: State, action: Action) -> Effect<Action> {
            switch action {
            case .dismiss:
                track(
                    Event(
                        category: .general,
                        actionName: .dismissTapped,
                        actionType: .tap,
                        elementType: .toolbarButton,
                        context: before.billingInfo.toString()
                    )
                )

            case .upgradeTapped:
                track(
                    Event(
                        category: .general,
                        actionName: .upgradeTapped,
                        actionType: .tap,
                        elementType: .button,
                        context: before.billingInfo.toString()
                    )
                )
            }
            return .none
        }
    }
}

extension RootTabCoordinator {
    struct Analytics: AnalyticsReducer {
        var source: String = "tab_bar"

        func analytics(before _: State, after _: State, action: Action) -> Effect<Action> {
            switch action {
            case .tabSelected(let tab):
                track(
                    Event(
                        category: .navigation,
                        actionName: .tabTapped,
                        actionType: .tap,
                        elementType: .tab,
                        elementId: tab.rawValue
                    )
                )

            case .createTapped:
                track(
                    Event(
                        category: .navigation,
                        actionName: .tabTapped,
                        actionType: .tap,
                        elementType: .tab,
                        elementId: "create"
                    )
                )

            default:
                break
            }
            return .none
        }
    }
}
