import AnalyticsClient
import ComposableArchitecture

public extension SongDetailsReducer {
    struct Analytics: AnalyticsReducer {
        public let source: String

        public init() {
            if Mode.self == PublishSongMode.self {
                source = "publish"
            } else if Mode.self == EditSongDetailsMode.self {
                source = "edit_song_details"
            } else {
                source = "song_details" // Shouldn't happen
            }
        }

        // swiftlint:disable:next cyclomatic_complexity function_body_length
        public func analytics(before: State, after: State, action: Action) -> Effect<Action> {
            switch action {
            case .saveTapped:
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .saveTapped,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId
                    )
                )

            case .internal(.saveResponse(.success)):
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .saveSongDetailsSucceeded,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId
                    )
                )

            case .internal(.saveResponse(.failure(let error))):
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .saveSongDetailsFailed,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId,
                        elementText: error.underlyingError
                    )
                )

            case .dismissTapped:
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .dismissTapped,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId
                    )
                )

            case .publishTapped:
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .publishTapped,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId
                    )
                )

            case .internal(.publishResponse(.success)):
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .publishSucceeded,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId
                    )
                )

            case .internal(.publishResponse(.failure(let error))):
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .publishFailed,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId,
                        elementText: error.underlyingError
                    )
                )

            case .editLyricsTapped:
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .editDisplayedLyricsTapped,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId
                    )
                )

            case .previewTapped:
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .previewTapped,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId
                    )
                )

            case .replaceSongArtTapped:
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .replaceSongArtTapped,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId
                    )
                )

            case .moreOptionsTapped:
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .moreSongDetailsOptionsTapped,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId
                    )
                )

            case .updateFields:
                let oldNameValue = before.clip.title.ifNotEmpty
                let newNameValue = after.clip.title.ifNotEmpty
                let oldCaptionValue = before.clip.caption?.ifNotEmpty
                let newCaptionValue = after.clip.caption?.ifNotEmpty

                // Track title and caption changes separately
                var effects: [Effect<Action>] = []

                // Handle title changes
                if oldNameValue != newNameValue {
                    let titleActionName: Event.ActionName = {
                        switch (oldNameValue, newNameValue) {
                        case (.none, .some): return .titleAdded
                        case (.some, .none): return .titleUpdated // Shouldn't happen, title is required
                        case (.some, .some): return .titleUpdated
                        case (.none, .none): return .titleUpdated // Should never happen
                        }
                    }()

                    effects.append(.run { [clip = before.clip] _ in
                        track(
                            Event(
                                category: .clipDetails,
                                actionName: titleActionName,
                                actionType: .textEntry,
                                elementType: .clip,
                                elementId: clip.id.remoteId,
                                elementText: newNameValue
                            )
                        )
                    })
                }

                // Handle caption changes
                if oldCaptionValue != newCaptionValue {
                    let captionActionName: Event.ActionName = {
                        switch (oldCaptionValue, newCaptionValue) {
                        case (.none, .some): return .captionAdded
                        case (.some, .none): return .captionDeleted
                        case (.some, .some): return .captionUpdated
                        case (.none, .none): return .captionUpdated // Should never happen
                        }
                    }()

                    effects.append(.run { [clip = before.clip] _ in
                        track(
                            Event(
                                category: .clipDetails,
                                actionName: captionActionName,
                                actionType: .textEntry,
                                elementType: .clip,
                                elementId: clip.id.remoteId,
                                elementText: newCaptionValue
                            )
                        )
                    })
                }

                return .merge(effects)

            // Edit displayed lyrics
            case .destination(.presented(.editDisplayedLyrics(.dismissTapped))):
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .editDisplayedLyricsDismissed,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId
                    )
                )

            case .destination(.presented(.editDisplayedLyrics(.saveTapped))):
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .editDisplayedLyricsSaveTapped,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId
                    )
                )

            case .destination(.presented(.editDisplayedLyrics(.updateLyrics))):
                let oldLyricsValue = before.clip.prompt.ifNotEmpty
                let newLyricsValue = after.clip.prompt.ifNotEmpty
                let lyricsActionName: Event.ActionName = {
                    switch (oldLyricsValue, newLyricsValue) {
                    case (.none, .some): return .displayedLyricsAdded
                    case (.some, .none): return .displayedLyricsDeleted
                    case (.some, .some): return .displayedLyricsUpdated
                    case (.none, .none): return .displayedLyricsUpdated // Should never happen
                    }
                }()

                track(
                    Event(
                        category: .clipDetails,
                        actionName: lyricsActionName,
                        actionType: .textEntry,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId,
                        elementText: newLyricsValue
                    )
                )

            case .destination(.presented(.editDisplayedLyrics(.internal(.updateLyricsResponse(.success))))):
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .editDisplayedLyricsSucceeded,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId
                    )
                )

            case .destination(.presented(.editDisplayedLyrics(.internal(.updateLyricsResponse(.failure(let error)))))):
                track(
                    Event(
                        category: .clipDetails,
                        actionName: .editDisplayedLyricsFailed,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId,
                        elementText: error.underlyingError
                    )
                )

            case .destination(.presented(.moreOptions(.updateAllowComments(let allowComments)))):
                track(
                    Event(
                        category: .clipDetails,
                        actionName: allowComments ? .commentsEnabled : .commentsDisabled,
                        actionType: .tap,
                        elementType: .clip,
                        elementId: before.clip.id.remoteId
                    )
                )

            default:
                break
            }
            return .none
        }
    }
}
