import APIClient
import CommentsClient
import ComponentLibrary
import ComposableArchitecture
import FeatureBrandedAlert
import FeatureToasts
import Localization
import OmniPlayerClient
import StatsigClient
import SwiftUI
import Utilities

extension CommentsSheetReducer {
    func subscribeToAllEvents() -> Effect<Action> {
        @Dependency(\.omniplayerClient.stream) var omniplayerStream

        return .merge(
            .stream(
                commentsSheetClient.stream(),
                send: Action.commentsSheetClient,
                cancellableId: CommentsSheetClientEventCancellableId()
            ),
            .stream(omniplayerStream(), send: Action.omniplayerEvent, cancellableId: ElapsedTrackTimeCancellableId())
        )
    }
}

extension CommentsSheetReducer {
    func handleCommentSheetClientEvent(_ event: CommentsClientV2.Event, state: inout State) -> Effect<Action> {
        /// Do not trigger the following actions here
        /// ```
        ///   case .loadNextPageOfCommentsForEntity
        ///   case .postComment
        ///   case .updateCommentReaction
        /// ```
        /// These will cause infinite recursion
        switch event {
        case .didUpdateComments(let commentsThreadMap):
            state.isRequestingComments = false
            if let commentThread = commentsThreadMap.threadForEntity(state.item.commentEntityId, entityType: state.item.commentEntityType) {
                var newComments = commentThread.allSortedUnparentedComments
                state.replyCommentsMap = commentThread.allSortedRepliesForComment
                state.hasMoreRepliesMap = commentThread.hasMoreRepliesMap

                positionCommentForDeeplinkIntoReplyIfNeeded(&newComments, state: &state)

                state.comments = newComments
            }
            return .none

        case .didReportComment:
            return .send(.toastAction(
                .show(.success(
                    "",
                    .string(L10n.FeatureComments.commentReported),
                    position: .bottom,
                    destination: nil,
                    trailingView: .dismiss
                ))
            ))

        case .didResolveTemporaryComment(let commentID):
            state.pendingComments.removeAll { $0.id == commentID }
            return .none

        case .didUpdateSuggestedUserMentions(let matchingUsers):
            state.userMentionSearchSuggestions = matchingUsers
            return .none

        case let .didReceiveAPIToastingError(userVisibleString):
            state.isRequestingComments = false
            return .send(
                .toastAction(.show(ToastReducer.State.ToastType.warning(userVisibleString)))
            )

        case let .didReceiveAPIModalError(userVisibleString):
            state.isRequestingComments = false
            return .send(
                .brandedAlert(.setStyle(
                    .singleButtonAlert(.custom(SingleButtonAlertStyle.TextCopy(
                        title: L10n.FeatureComments.jailTitle,
                        description: userVisibleString,
                        buttonLabel: L10n.FeatureComments.jailButtonTitle
                    )))
                ))
            )
        }
    }
}

extension CommentsSheetReducer {
    func handleOmniplayerClientEvent(_ event: OmniPlayerEvent, state _: inout State) -> Effect<Action> {
        switch event {
        case .playbackTimeUpdated(currentTime: let time):
            return .send(.internal(.periodicTimeResponse(time)))
        default:
            return .none
        }
    }
}

private extension CommentsSheetReducer {
    func positionCommentForDeeplinkIntoReplyIfNeeded(_ comments: inout [CommentEntity], state: inout State) {
        if let replyTargetId = state.replyToCommentID,
           let targetComment = comments.first(where: { $0.id == replyTargetId })
        {
            // Move target comment to top for visibility
            comments.removeAll { $0.id == replyTargetId }
            comments.insert(targetComment, at: 0)

            state.currentReplyParentCommentID = replyTargetId
            state.replyToCommentID = nil
        } else if let currentReplyId = state.currentReplyParentCommentID,
                  let currentReplyComment = comments.first(where: { $0.id == currentReplyId })
        {
            // Preserve existing reply target at top during background updates
            comments.removeAll { $0.id == currentReplyId }
            comments.insert(currentReplyComment, at: 0)
        }
    }
}
