import APIClient
import CommentsClient
import ComponentLibrary
import ComposableArchitecture
import CoreMedia
import FeatureBrandedAlert
import FeatureCaptions
import FeatureToasts
import Localization
import StatsigClient
import SwiftUI
import Utilities

public struct CommentsThreadView: View {
    @Bindable var store: StoreOf<CommentsThread>
    @State var currentDetent = PresentationDetent.medium

    @FocusState private var focusedTextField

    public init(store: StoreOf<CommentsThread>) {
        self.store = store
    }

    public var body: some View {
        VStack(spacing: .zero) {
            HStack {
                Group {
                    switch store.totalCommentCount {
                    case .known(let count):
                        if count == .zero {
                            Text(L10n.FeatureComments.comments)
                                .typographyV1(.caption2)
                        } else {
                            Text(L10n.FeatureComments.numComments(count))
                                .typographyV1(.caption2)
                        }

                    case .unknown:
                        Text(L10n.FeatureComments.comments)
                            .typographyV1(.caption2)
                    }
                }
                .padding(.top, 24.0)
            }
            .frame(height: 42.0)
            .padding(.bottom, 8.0)

            Divider()
                .foregroundStyle(Color.SemanticV1.textTertiary)

            ScrollView {
                Color.clear
                    .frame(height: 22.0)

                if store.isLoading {
                    loadingStack
                } else if store.noCommentsAndNoAuthorCaption {
                    emptyStack
                        .padding(.top, 64.0)
                        .opacity(.half)
                } else {
                    if let caption = store.authorCaption {
                        PinnedCaptionView(
                            caption,
                            userMentionTapped: { handle in
                                store.send(.didTapUserHandle(handle))
                            }
                        )
                        .padding(.top, -20)
                    }
                    scrollingCommentsThread
                }

                if store.hasReplyComment {
                    Color.clear
                        .frame(height: 42.0)
                }
            }
            .overlay(alignment: .bottom) {
                ToastView(store: store.scope(state: \.toastState, action: \.toastAction))

                HStack {
                    Text(store.replyRootCommentUserDisplayName)
                        .typographyV1(.caption3)
                        .foregroundStyle(Color.SemanticV1.textSecondary)
                        .padding(.leading, 24.0)

                    if store.hasReplyComment {
                        Text(store.replyRootCommentContent)
                            .typographyV1(.caption3)
                            .foregroundStyle(Color.SemanticV1.textTertiary)
                            .padding(.leading, 8.0)
                            .lineLimit(1)
                            .opacity(.half)
                    }

                    Spacer()

                    Image.Icon.close
                        .resizable()
                        .foregroundStyle(Color.SemanticV1.textSecondary)
                        .frame(width: 16.0, height: 16.0)
                        .padding(.trailing, 24.0)
                        .contentShape(.rect)
                        .onTapGesture {
                            store.send(.clearReplyTarget)
                        }
                }
                .frame(height: 42.0)
                .background {
                    Color.SemanticV1.backgroundTertiary
                }
                .offset(y: store.hasReplyComment ? 0.0 : 8.0)
                .opacity(store.hasReplyComment ? 1.0 : 0.0)
                .animation(.easeInOut(duration: 0.15), value: store.hasReplyComment)
            }
            .clipped()
        }
        .safeAreaInset(edge: .bottom, spacing: 0) {
            bottomBarView
        }
        .overlay {
            if let _ = store.state.brandedAlert.destination {
                ZStack {
                    Color.SemanticV1.backgroundQuaternary.opacity(0.4)
                        .ignoresSafeArea()
                    BrandedAlertView(store.scope(state: \.brandedAlert, action: \.brandedAlert))
                }
            }
        }
        .presentationCornerRadius(40, conditional: true)
        .presentationDetents([.medium, .large], selection: $currentDetent)
        .task {
            store.send(.task)
        }
        .onAppear {
            store.send(.onAppear)
        }
        .onChange(of: store.currentReplyParentCommentID) { _, newValue in
            // Open keyboard when routing into a reply to a comment
            if newValue != nil {
                focusOnTextField()
            }
        }
    }

    /// Required to avoid visual artifacts when focusing on text field
    func focusOnTextField() {
        Task {
            if currentDetent == .medium {
                currentDetent = .large
                try? await Task.sleep(for: .seconds(0.05))
            }
            focusedTextField = true
        }
    }

    var loadingStack: some View {
        VStack {
            ProgressView()
                .progressViewStyle(.circular)
                .padding(.top, 64.0)
        }
    }

    @ViewBuilder
    var emptyStack: some View {
        VStack {
            Text(L10n.FeatureComments.exclaimNoCommentsYet)
                .typographyV1(.headline3)
                .multilineTextAlignment(.center)
            Text(L10n.FeatureComments.showYourLoveCopy)
                .typographyV1(.caption2)
                .frame(width: 250.0)
                .multilineTextAlignment(.center)
        }
        .foregroundStyle(Color.SemanticV1.textPrimary)
    }

    @ViewBuilder
    var scrollingCommentsThread: some View {
        LazyVStack {
            ForEach(Array(store.pendingComments.enumerated()), id: \.element) { _, comment in
                /*
                    Not using commentComponent helper function
                    here since it is a highly limited version
                 */
                CommentThreadItem(
                    comment,
                    isCommentByCurrentUser: comment.userHandle == store.currentUser.handle,
                    isCommentByClipAuthor: comment.userHandle == store.authorUserHandle,
                    isClipByCurrentUser: store.isClipAuthorCurrentUser,
                    isLoading: true,
                    tapTrackTimestamp: { seconds in
                        store.send(.tapCommentTrackTimestamp(seconds))
                    }
                )
                .padding(.bottom, 16.0)
                .opacity(.half)
            }

            ForEach(Array(store.comments.enumerated()), id: \.element) { index, comment in
                let replies = store.replyCommentsMap[comment.id] ?? []
                commentComponent(
                    comment: comment,
                    isCommentByClipAuthor: comment.userHandle == store.authorUserHandle,
                    onAppear: {
                        guard !store.isRequestingComments,
                              index >= store.infiniteScrollLimit,
                              index > store.greatestRequestIndex
                        else { return }
                        store.send(.onItemAppeared(index))
                    }
                )
                .padding(.bottom, replies.isEmpty ? 16.0 : 8.0)

                let hasMoreReplies = store.hasMoreRepliesMap[comment.id] ?? false
                ForEach(Array(replies.enumerated()), id: \.element) { index, reply in
                    let isLastReply = (replies.count - 1) == index
                    let remainingReplies = max(0, comment.numReplies - replies.count)
                    commentComponent(
                        comment: reply,
                        isCommentByClipAuthor: reply.userHandle == store.authorUserHandle,
                        parentReplyCount: remainingReplies,
                        isFirstReply: index == .zero,
                        isLastReply: isLastReply,
                        hasMoreReplies: comment.numReplies > replies.count && hasMoreReplies,
                        onTapLoadMoreReplies: {
                            store.send(.loadNextReplyPageOfComments(comment.id))
                        },
                        onAppear: { /* No automatic paging on replies */ }
                    )
                    .padding(.leading, 24.0)
                    .padding(.bottom, isLastReply ? 8.0 : 0.0)
                }
            }

            Color.clear
                .frame(height: 32.0)
        }
        .padding(.leading, 16.0)
        .padding(.trailing, 8.0)
    }

    @ViewBuilder
    func commentComponent(
        comment: ClipComment,
        isCommentByClipAuthor: Bool = false,
        parentReplyCount: Int = .zero,
        isFirstReply: Bool = false,
        isLastReply: Bool = false,
        hasMoreReplies: Bool = false,
        onTapLoadMoreReplies: @escaping () -> Void = {},
        onAppear: @escaping () -> Void
    ) -> some View {
        CommentThreadItem(
            comment,
            isCommentByCurrentUser: store.currentUser.id == comment.userID,
            isCommentByClipAuthor: isCommentByClipAuthor,
            isClipByCurrentUser: store.isClipAuthorCurrentUser,
            parentReplyCount: parentReplyCount,
            isFirstReply: isFirstReply,
            isLastReply: isLastReply,
            hasMoreReplies: hasMoreReplies,
            hasExpandedLongComment: store.hasExpandedComment[comment.id] ?? false,
            tapAuthor: {
                store.send(.didTapUserHandle(comment.userHandle))
            },
            tapMentionedUser: { userHandle in
                store.send(.didTapUserHandle(userHandle))
            },
            tapReaction: {
                switch comment.reaction {
                case .like:
                    store.send(.updateCommentReaction(comment.id, .removeReaction, comment.numLikes))
                case .dislike, .removeReaction:
                    store.send(.updateCommentReaction(comment.id, .like, comment.numLikes))
                }
            },
            tapReplyAction: {
                store.send(.setReplyTarget(comment.id))
            },
            tapTrackTimestamp: { seconds in
                store.send(.tapCommentTrackTimestamp(seconds))
            },
            tapLoadMoreReplies: {
                onTapLoadMoreReplies()
            },
            tapExpandOnLongComment: {
                store.send(.expandedComment(comment.id))
            },
            reportAction: { reason in
                store.send(.reportComment(commentID: comment.id, reason: reason))
            },
            deleteAction: {
                store.send(.deleteComment(comment.id))
            },
            onAppear: onAppear
        )
    }
}

extension CommentsThreadView {
    private var bottomBarView: some View {
        VStack(spacing: .zero) {
            Divider()
                .foregroundStyle(Color.SemanticV1.textTertiary)
            ZStack {
                if store.areCommentsEnabled {
                    bottomBarContent
                } else {
                    bottomBarDisabledContent
                }
            }
            .opacity(store.isLoading ? 0 : 1)
        }
        .clipped()
        .animation(.spring(duration: 0.25), value: store.state.userMentionSearchSuggestions.isEmpty)
        // Allow user to dismiss the keyboard by swipping down on the bottom bar
        .gesture(DragGesture().onEnded { gesture in
            if gesture.translation.height > 20 {
                // Use swipe down distance of 20 to avoid accidentally closing the keyboard
                withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
                    focusedTextField = false
                }
            }
        })
    }

    @ViewBuilder
    var bottomBarDisabledContent: some View {
        let description = "Comments are disabled for this clip."
        let backgroundColor: Color = .SemanticV1.backgroundQuaternary

        backgroundColor
            .frame(height: 64.0)
            .ignoresSafeArea()
        Text(description)
            .typographyV1(.bodySmall)
            .foregroundStyle(Color.SemanticV1.textTertiary)
    }

    var bottomBarContent: some View {
        VStack(spacing: .zero) {
            if !store.state.userMentionSearchSuggestions.isEmpty {
                userMentionSearchList
                    .transition(.blurReplace)
            } else {
                emojiScrollArea
                    .padding(.top, store.areCommentsEnabled ? 8.0 : 0.0)
                    .padding(.bottom, 8.0)
                    .transition(.blurReplace)
            }
            textFieldStack
                .padding(.horizontal, 16.0)
                .frame(height: 64.0)
        }
    }

    var textFieldStack: some View {
        HStack {
            userAvatarImage

            textFieldButton

            openUserMentionSearchButton
        }
    }

    @ViewBuilder
    var textFieldButton: some View {
        ZStack {
            Button(action: focusOnTextField) {}
                .buttonStyle(PillButtonStyleV1(size: .textFieldButton, colorCombination: .textFieldButton))
                .accessibilityLabel(L10n.FeatureComments.ellipsesAddAComment)
                .accessibilityHidden(focusedTextField)

            textFieldCapsule
                .allowsHitTesting(focusedTextField)
                .accessibilityHidden(!focusedTextField)
        }
    }

    @ViewBuilder
    var textFieldCapsule: some View {
        let showingQuickSendButton = !store.userCommentText.isEmpty

        HStack(spacing: 0) {
            textField
                .padding(.leading, 16)
                .padding(.trailing, -8)

            timeElapsedText
                .padding(.trailing, showingQuickSendButton ? -4 : 10)
                .padding(.leading, 10)

            if showingQuickSendButton {
                quickSendButton
                    /// to align exactly with the rounded rectangle background of the `textFieldStack`
                        .offset(x: 5)
            }
        }
        .frame(height: 44.0)
    }

    @ViewBuilder
    private var emojiScrollArea: some View {
        let emojis: [String] = [
            "🔥", "😍", "😱", "🙌", "👍", "👎", "🥵", "😎",
            "😄", "😁", "🎉", "😂", "🎊", "👏", "🕺", "😊",
            "💃", "🤣", "😃", "😀", "🤗", "😆", "🥳", "🤩",
            "🥰",
        ]
        CommentEmojiBar(emojis: emojis) { emoji in
            store.send(.addSingleEmoji(emoji))
        }
    }

    @ViewBuilder
    private var userAvatarImage: some View {
        RemoteImage(url: store.currentUser.avatarImageUrl, fallbackId: "1")
            .frame(width: 40, height: 40)
            .clipShape(Circle())
    }

    @ViewBuilder
    private var textField: some View {
        TextField(
            "",
            text: $store.userCommentText.limit(CommentsClient.commentLengthLimit),
            prompt: Text(L10n.FeatureComments.ellipsesAddAComment),
            axis: .vertical
        )
        .typographyV1(.caption)
        .lineLimit(3)
        .submitLabel(.send)
        .focused($focusedTextField)
        .onSubmit {
            store.send(.postComment)
        }
        .onChange(of: store.userCommentText) { _, newValue in
            store.send(.onUserCommentTextUpdated(newValue))
        }
    }

    @ViewBuilder
    private var timeElapsedText: some View {
        Text(store.commentTrackTime.format_m_ss)
            .typographyV1(.caption)
            .foregroundColor(.SemanticV2.foregroundInactive)
            .lineLimit(1)
    }

    @ViewBuilder
    private var quickSendButton: some View {
        Button {
            store.send(.postComment)
        } label: {
            Image.Icon.backButtonV1.rotationEffect(.degrees(90.0))
                .foregroundStyle(Color.SemanticV1.textInvert)
                .padding(.horizontal, 16.0)
                .frame(height: 38)
                .background(Color.SemanticV1.textPrimary)
                .clipShape(Circle())
        }
        .buttonStyle(.plain)
    }
}

extension CommentsThreadView {
    private var userMentionSearchList: some View {
        let count = store.userMentionSearchSuggestions.count
        let isScrollable = count >= 4
        let itemHeight: CGFloat = 56
        let topBottomPadding: CGFloat = isScrollable ? 8 : 6
        let listHeight = CGFloat(count) * itemHeight + (topBottomPadding * 2)

        return ScrollView {
            LazyVStack(spacing: .zero) {
                ForEach(Array(store.userMentionSearchSuggestions.enumerated()), id: \.element) { index, user in
                    let isMiddleItem = index < count - 1

                    userMentionSearchListItem(user, showDivider: isMiddleItem)
                        .frame(maxWidth: .infinity, alignment: .leading)
                        .padding(.horizontal, 16)
                        .contentShape(Rectangle())
                        // Use tap gesture with simultaneous drag gesture to avoid accidentally
                        // selecting a list item when lifting finger off list
                        .onTapGesture {
                            store.send(.onUserMentionSearchItemTapped(user))
                        }
                }
            }
            .padding(.vertical, topBottomPadding)
            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
        }
        .background(Color.SemanticV2.backgroundFogThin)
        .frame(height: min(listHeight, UIScreen.height * 0.3))
        .overlay(alignment: .bottom) {
            if isScrollable {
                LinearGradient(colors: [.black.opacity(0), .black], startPoint: .top, endPoint: .bottom)
                    .blendMode(.destinationOut)
                    .frame(height: 40)
                    .allowsHitTesting(false)
            }
        }
        .compositingGroup()
    }

    private func userMentionSearchListItem(
        _ user: SimpleProfile,
        showDivider: Bool = true
    ) -> some View {
        VStack(spacing: .zero) {
            HStack(spacing: 12) {
                RemoteImage(url: user.avatarImageUrl, fallbackId: nil)
                    .frame(width: 32, height: 32)
                    .clipShape(Circle())

                VStack(alignment: .leading, spacing: .zero) {
                    if !user.displayName.isEmpty {
                        Text(user.displayName)
                            .typographyV1(.body3)
                            .foregroundColor(.SemanticV1.textPrimary)
                            .lineLimit(1)
                    }
                    Text("@" + user.handle)
                        .typographyV1(.caption)
                        .foregroundColor(.SemanticV1.textTertiary)
                        .lineLimit(1)
                }
                .frame(maxWidth: .infinity, alignment: .leading)
            }
            .contentShape(.rect)
            .padding(.vertical, 10)

            if showDivider {
                Divider()
                    .foregroundStyle(Color.SemanticV1.textTertiary)
            }
        }
    }

    private var openUserMentionSearchButton: some View {
        Button {
            store.send(.openUserMentionSearch)
            focusOnTextField()
        } label: {
            Image.Icon.at
                .renderingMode(.template)
                .foregroundStyle(Color.SemanticV1.textTertiary)
                .padding(.horizontal, 10.0)
                .frame(height: 36)
                .clipShape(Circle())
        }
        .buttonStyle(.plain)
    }
}

extension PillButtonSizeV1 {
    static let textFieldButton = PillButtonSizeV1(
        typography: .button1,
        width: nil,
        maxWidth: .infinity,
        minHeight: 44,
        iconWidth: 0,
        borderRadius: 32,
        padding: .zero
    )
}

extension PillButtonStyleV1.ColorCombination {
    static let textFieldButton = PillButtonStyleV1.ColorCombination(
        enabled: PillButtonStyleV1.ColorSet(
            foreground: .SemanticV1.textPrimary,
            background: .SemanticV2.backgroundFogThin,
            loading: .clear,
            border: .clear
        ),
        pressed: PillButtonStyleV1.ColorSet(
            foreground: .SemanticV1.textPrimary,
            background: .SemanticV2.backgroundFogThick,
            loading: .clear,
            border: .clear
        ),
        disabled: PillButtonStyleV1.ColorSet(
            foreground: .SemanticV1.textPrimary.opacity(0.5),
            background: .SemanticV1.backgroundSecondary,
            loading: .clear,
            border: .clear
        )
    )
}

#if DEBUG
    #Preview("Empty") {
        let state = CommentsThread.State(currentUser: User.mock(), clipID: ClipID(remoteId: UUID().uuidString), isClipAuthorCurrentUser: false)

        CommentsThreadView(store: Store(initialState: state, reducer: {}))
    }

    #Preview("Report") {
        let state = CommentsThread.State(currentUser: User.mock(), clipID: ClipID(remoteId: UUID().uuidString), isClipAuthorCurrentUser: false, comments: [
            ClipComment.mock(),
        ])

        CommentsThreadView(store: Store(initialState: state, reducer: {}))
    }

#endif
