import APIClient
import ComponentLibrary
import ComposableArchitecture
import InAppNotificationClient
import StatsigClient
import SwiftUI
import Utilities

@Reducer
public struct InAppNotificationList {
    @ObservableState
    public struct State: Equatable {
        @Shared(.inMemory(.inAppNotificationsState)) var inAppNotificationsState: NotificationLoadingState = .loading
        @Shared(.inMemory(.inAppNotificationMap)) var inAppNotificationMap: OrderedNotificationMap = .defaultValue
        @Shared(.inMemory(.hasUnreadNotifications)) var hasUnreadNotifications: Bool = false
        @Shared(.appStorage(.dismissedAnnouncements)) var dismissedAnnouncements: [String] = []

        var loadingClipOrPlaylistId: String?
        var followHandleInProgress: String?
        var lastAnnouncement: NotificationItem?

        public init() {}
    }

    public enum Action {
        public enum Delegate {
            case followTapped(_ handle: String, _ isFollowing: Bool)
            case userTapped(MiniProfile)
            case clipOrPlaylistTapped(MiniClipOrPlaylist, notificationId: String)
            case redirectV2(InAppNotificationItem.RedirectStyle, notificationId: String)
        }

        case delegate(Delegate)
        case followTapped(_ handle: String, _ isFollowing: Bool)
        case userTapped(MiniProfile)
        case clipOrPlaylistTapped(MiniClipOrPlaylist, notificationId: String)
        case redirectV2(InAppNotificationItem.RedirectStyle, notificationId: String)
    }

    @Dependency(\.dismiss) private var dismiss
    @Dependency(APIClient.self) private var apiClient
    @Dependency(\.inAppNotificationClient) var inAppNotificationClient

    public init() {}

    public var body: some ReducerOf<Self> {
        Reduce<State, Action> { _, action in
            switch action {
            case .clipOrPlaylistTapped(let clipPlaylist, let notificationId):
                return .send(.delegate(.clipOrPlaylistTapped(clipPlaylist, notificationId: notificationId)))

            case .followTapped(let handle, let isFollowing):
                return .send(.delegate(.followTapped(handle, isFollowing)))

            case .redirectV2(let redirectStyle, let notificationId):
                return .send(.delegate(.redirectV2(redirectStyle, notificationId: notificationId)))

            case .userTapped(let miniProfileV1):
                return .send(.delegate(.userTapped(miniProfileV1)))

            case .delegate:
                return .none
            }
        }
    }
}

public struct InAppNotificationListView: View {
    @Bindable var store: StoreOf<InAppNotificationList>
    @Namespace var namespace

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

    public var body: some View {
        notificationList(store.inAppNotificationMap)
    }

    @ViewBuilder
    func notificationList(_ notifications: OrderedNotificationMap) -> some View {
        ForEach(OrderedNotificationMap.OrderCategory.orderedCases) { category in
            let items = notifications.itemsForCategory(category)
            if let firstElement = items.first {
                switch firstElement {
                case .v1:
                    v1NotificationUI(key: category.displayName, value: notifications.itemsForCategory(category))
                case .v2:
                    v2NotificationUISection(key: category.displayName, value: notifications.itemsForCategory(category))
                }
            }
        }
    }

    @ViewBuilder
    // swiftlint:disable:next cyclomatic_complexity
    func v1NotificationUI(key: String, value: [InAppNotificationItem]) -> some View {
        let items = value
            .compactMap {
                switch $0 {
                case .v1(let item):
                    return item
                case .v2:
                    return nil
                }
            }
            .filter {
                // Don't show announcement row if already presented as banner
                if $0.id == store.lastAnnouncement?.id { return false }
                if $0.notificationType == .announcement { return true }
                return !$0.userProfiles.isEmpty
            }

        if !items.isEmpty {
            Section(header: header(title: key)) {
                VStack(spacing: 0) {
                    ForEach(items, id: \.self) { notification in
                        switch notification.notificationType {
                        case .clipLike, .sceneLike, .playlistLike:
                            if let user = notification.userProfiles.first {
                                LikedListItemView(
                                    type: notification.notificationType,
                                    user: user,
                                    totalUsers: notification.totalUsers,
                                    clipOrPlaylist: notification.reactContent,
                                    formattedCreateAt: notification.formattedCreateAt,
                                    userTapped: { store.send(.userTapped($0)) },
                                    clipOrPlaylistTapped: { store.send(.clipOrPlaylistTapped($0, notificationId: notification.id)) },
                                    clipOrPlaylistLoading: notification.reactContent?.id == store.loadingClipOrPlaylistId
                                )
                                .listRowBackground(Color.clear)
                                .overlay(alignment: .bottom) { divider }
                            } else {
                                EmptyView()
                            }

                        case .follow:
                            if let user = notification.userProfiles.first {
                                FollowListItemView(
                                    user: user,
                                    totalUsers: notification.totalUsers,
                                    formattedCreateAt: notification.formattedCreateAt,
                                    followTapped: {
                                        store.send(.followTapped($0.handle, $0.isFollowing))
                                    },
                                    followInProgress: store.followHandleInProgress == user.handle,
                                    userTapped: { store.send(.userTapped($0)) }
                                )
                                .listRowBackground(Color.clear)
                                .overlay(alignment: .bottom) { divider }
                            } else {
                                EmptyView()
                            }

                        case .announcement:
                            if let message = notification.message, !message.isEmpty {
                                AnnouncementListItemView(
                                    message: message,
                                    formattedCreateAt: notification.formattedCreateAt,
                                    clipOrPlaylist: notification.reactContent,
                                    clipOrPlaylistTapped: { store.send(.clipOrPlaylistTapped($0, notificationId: notification.id)) },
                                    clipOrPlaylistLoading: notification.reactContent?.id == store.loadingClipOrPlaylistId
                                )
                                .listRowBackground(Color.clear)
                                .overlay(alignment: .bottom) { divider }
                            } else {
                                EmptyView()
                            }

                        case .unfollow, .clipUnlike, .sceneUnlike, .playlistUnlike:
                            EmptyView()

                        case .clipComment,
                             .codeRedeem,
                             .commentLike,
                             .commentReply,
                             .commentMention,
                             .captionMention,
                             .defaultValue,
                             .delete,
                             .invite,
                             .inviteAccepted,
                             .personaFavorite,
                             .personaFollow,
                             .personaUsed,
                             .playlistComment,
                             .sceneComment,
                             .clipCreateFollowee,
                             .restrictedByDefault,
                             .clipRemix,
                             .hookComment,
                             .hookCommentLike,
                             .hookCommentMention,
                             .hookCommentReply,
                             .hookCreated,
                             .hookLike,
                             .videoCoverHookLike,
                             .videoCoverHookComment,
                             .videoCoverHookCommentLike,
                             .videoCoverHookCommentMention,
                             .videoCoverHookCommentReply:
                            /* No-Op */
                            EmptyView()
                        }
                    }
                }
            }
            .textCase(nil)
            .listSectionSeparator(.hidden)
        }
    }

    @ViewBuilder
    func v2NotificationUISection(key: String, value: [InAppNotificationItem]) -> some View {
        let v2ItemsOnly: [InAppNotificationItem] = value.compactMap { item in
            guard case .v2 = item else { return nil }

            // Filter out hook notifications if hooks feature flag is disabled
            if !FeatureFlag.hooks.isFeedEnabled {
                switch item.notificationType {
                case .hookComment,
                     .hookCommentLike,
                     .hookCommentMention,
                     .hookCommentReply,
                     .hookCreated,
                     .hookLike,
                     .videoCoverHookLike,
                     .videoCoverHookComment,
                     .videoCoverHookCommentLike,
                     .videoCoverHookCommentMention,
                     .videoCoverHookCommentReply:
                    return nil
                default:
                    break
                }
            }
            return item
        }

        if !v2ItemsOnly.isEmpty {
            Section(header: header(title: key)) {
                ForEach(v2ItemsOnly) { item in
                    switch item {
                    case .v1:
                        EmptyView()
                    case .v2(let notification):
                        var userToFollow: MiniProfileV2? {
                            /*
                                Only accounts for single user follow
                                Will do follow up for multiple user follow list navigation.
                             */
                            guard
                                case .follow = item.notificationType,
                                notification.userProfiles.count == 1,
                                let userProfile = notification.userProfiles.first
                            else { return nil }
                            return userProfile
                        }

                        NotificationContentV2Stack(
                            notification,
                            followProgressHandle: store.followHandleInProgress,
                            userToFollow: userToFollow,
                            onTappedFollow: { profile in
                                Task { @MainActor [profile] in
                                    store.send(.followTapped(profile.handle ?? "", profile.isFollowing))
                                }
                            },
                            onTappedProfile: {
                                Task { @MainActor [redirectStyle = item.profileRedirectStyle] in
                                    store.send(.redirectV2(redirectStyle, notificationId: item.id))
                                }
                            },
                            onTappedContent: {
                                Task { @MainActor [redirectStyle = item.primaryRedirectStyle] in
                                    store.send(.redirectV2(redirectStyle, notificationId: item.id))
                                }
                            }
                        )
                        .padding(.vertical, 16.0)
                        .padding(.horizontal, 8.0)
                    }
                }
            }
            .textCase(nil)
            .listSectionSeparator(.hidden)
        }
    }

    var divider: some View {
        Divider()
            .overlay(Color.SemanticV1.borderPrimary)
            .padding(.horizontal, -12) // Negative padding to fill width of screen
    }

    func header(title: String, action _: (() -> Void)? = nil) -> some View {
        HStack(spacing: 8) {
            Text(title)
                .typographyV1(.headline4Wide)
                .foregroundStyle(Color.SemanticV1.textPrimary)
            Spacer()
        }
        .padding(.bottom, 8)
        .padding(.top, 16)
    }
}
