import APIClient
import ComponentLibrary
import ComposableArchitecture
import ContactsClient
import Localization
import NavigationRouterClient
import StatsigClient
import SwiftUI
import Utilities

@Reducer
public struct CreatorsToFollow {
    @Reducer(state: .equatable)
    public enum Destination {
        case fromYourContacts(FromYourContacts)
    }

    @ObservableState
    public struct State: Equatable {
        var recommendedUsers: IdentifiedArrayOf<RecommendUser> = []
        var followRequestsInFlight: IdentifiedArrayOf<RecommendUser> = []

        var loadState: LoadState = .loading

        var isContactSyncEnabled: Bool {
            FeatureFlag.legacy.contactSync
        }

        @Shared(.appStorage(.dismissedContactSyncCreatorsToFollow)) var dismissedContactSyncBanner: Bool = false

        var showContactSyncBanner: Bool {
            guard isContactSyncEnabled else { return false }
            @Dependency(\.contactsClient.getAuthorizationStatus) var getContactsAuthorizationStatus

            if !dismissedContactSyncBanner && !(getContactsAuthorizationStatus() == .authorized) {
                return true
            }
            return false
        }

        @ObservationStateIgnored @ObservedBox var contactSyncBanner = ContactSyncBanner.State()
        @Presents public var destination: Destination.State?

        @Shared var me: Me

        let showBackButton: Bool

        public init(me: Shared<Me>, showBackButton: Bool = true) {
            self._me = me
            self.showBackButton = showBackButton
        }
    }

    public enum Action {
        public enum Internal {
            case recommendUsersResult(Result<[RecommendUser], Error>)
            case followResult(RecommendUser, Result<Void, Error>)
            case fetchContactPhoneNumbersResult(Result<[String], Error>)
        }

        case `internal`(Internal)
        case destination(PresentationAction<Destination.Action>)

        case backButtonTapped
        case onAppear
        case followTapped(RecommendUser)
        case creatorRowTapped(RecommendUser)
        case contactSyncBanner(ContactSyncBanner.Action)
    }

    public init() {}

    @Dependency(\.dismiss) var dismiss
    @Dependency(APIClientV2.self) var api
    @Dependency(APIClient.self) private var apiClient
    @Dependency(ContactsClient.self) private var contactsClient
    @Dependency(NavigationRouterClient.self) private var navigationRouter

    public var body: some ReducerOf<Self> {
        Scope(state: \.contactSyncBanner, action: \.contactSyncBanner) {
            ContactSyncBanner()
        }
        Reduce { state, action in
            switch action {
            case .onAppear:
                return .run { [isContactSyncEnabled = state.isContactSyncEnabled] send in
                    if isContactSyncEnabled && contactsClient.getAuthorizationStatus() == .authorized {
                        await send(.internal(.fetchContactPhoneNumbersResult(Result(catching: { try await contactsClient.getContactPhoneNumbers() }))))
                    } else {
                        await send(.internal(.recommendUsersResult(Result(catching: { try await api.getRecommendedUsers([]) }))))
                    }
                }

            case .internal(.fetchContactPhoneNumbersResult(.success(let phoneNumbers))):
                return .run { send in
                    await send(.internal(.recommendUsersResult(Result(catching: { try await api.getRecommendedUsers(phoneNumbers) }))))
                }

            case .internal(.fetchContactPhoneNumbersResult(.failure(let error))):
                log.telemetry.error(error)
                // If contact fetch fails, still get recommended users without them
                return .run { send in
                    await send(.internal(.recommendUsersResult(Result(catching: { try await api.getRecommendedUsers([]) }))))
                }

            case .internal(.recommendUsersResult(.success(let recommendUsers))):
                state.loadState = .loaded
                state.recommendedUsers = .init(uniqueElements: recommendUsers)
                return .none

            case .internal(.recommendUsersResult(.failure(let error))):
                state.loadState = .failed(L10n.FeatureSocial.creatorsToFollowLoadError)
                log.telemetry.error(error)
                return .none

            case .backButtonTapped:
                return .run { _ in await self.dismiss() }

            case .followTapped(let recommendedUser):
                state.followRequestsInFlight.append(recommendedUser)
                return .run { [handle = recommendedUser.user.handle, isFollowing = recommendedUser.user.isFollowing] send in
                    await send(.internal(.followResult(recommendedUser, Result(catching: { try await apiClient.profileFollow(handle, isFollowing) }))))
                }

            case .creatorRowTapped(let recommendedUser):
                navigationRouter.send(.profile(
                    recommendedUser.user.handle,
                    recommendationMetadata: nil,
                    simpleProfile: recommendedUser.user
                ))
                // Nav V1 can't present the profile screen without sorting through a circular dependency
                return .none

            case .contactSyncBanner(.delegate(.bannerTappedAndAuthorized)):
                state.destination = .fromYourContacts(.init(me: state.me))
                return .none

            case .contactSyncBanner(.delegate(.dismissTapped)):
                state.$dismissedContactSyncBanner.withLock { $0 = true }
                return .none

            case .contactSyncBanner:
                return .none

            case .internal(.followResult(let recommendedUser, .success)):
                state.followRequestsInFlight.remove(recommendedUser)
                var mutableRecommendedUser = recommendedUser
                mutableRecommendedUser.user.isFollowing.toggle()
                state.recommendedUsers[id: mutableRecommendedUser.id] = mutableRecommendedUser
                return .none

            case let .internal(.followResult(recommendedUser, .failure(error))):
                state.followRequestsInFlight.remove(recommendedUser)
                log.telemetry.error(error)
                return .none

            case .destination:
                return .none
            }
        }
        .ifLet(\.$destination, action: \.destination)

        Analytics()
    }
}

public struct CreatorsToFollowScreen: View {
    @Bindable public var store: StoreOf<CreatorsToFollow>

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

    public var body: some View {
        Group {
            switch store.loadState {
            case .loading:
                LoadingView()
            case .loaded:
                loadedView()
            case .failed(let message):
                FailedView(title: L10n.FeatureSocial.errorTitle, message: message, buttonTitle: L10n.FeatureSocial.retry, action: { store.send(.onAppear) })
            }
        }
        .onAppear {
            store.send(.onAppear)
        }
        .modifier(if: store.showBackButton) {
            $0.customBackButton(background: Color.SemanticV1.backgroundTertiary) { store.send(.backButtonTapped) }
        }
        .navigationTitle(L10n.FeatureSocial.creatorsToFollowTitle)
        .navigationBarTitleDisplayMode(.inline)
        .background(Color.SemanticV1.backgroundPrimary)
        .fullScreenCover(item: $store.scope(state: \.destination?.fromYourContacts, action: \.destination.fromYourContacts)) { store in
            NavigationStack {
                FromYourContactsScreen(store: store)
                    .toolbar {
                        ToolbarItem(placement: .topBarTrailing) {
                            ToolbarButton(.close, background: Color.SemanticV1.backgroundQuaternary) {
                                store.send(.dismiss)
                            }
                        }
                    }
            }
        }
    }

    @ViewBuilder
    private func loadedView() -> some View {
        if store.recommendedUsers.isEmpty {
            Text(L10n.FeatureSocial.creatorsToFollowEmptyTitle)
                .typographyV1(.headline4)
                .foregroundStyle(Color.SemanticV1.textSecondary)
                .frame(maxWidth: .infinity, maxHeight: .infinity)
        } else {
            List {
                Group {
                    if store.showContactSyncBanner {
                        Section {
                            ContactSyncBannerView(store: store.scope(state: \.contactSyncBanner, action: \.contactSyncBanner))
                        }
                        .listSectionSeparator(.hidden)
                        .listRowBackground(Color.clear)
                        .listSectionSpacing(16)
                        .padding(.top, 16)
                    }
                    Section {
                        VStack(spacing: 0) {
                            ForEach(store.recommendedUsers) { recommendedUser in
                                creatorsToFollowRow(recommendedUser)
                                    .alignmentGuide(.listRowSeparatorLeading) { d in
                                        d[.leading]
                                    }
                                    .onTapGesture {
                                        store.send(.creatorRowTapped(recommendedUser))
                                    }

                                divider
                            }
                        }
                    }
                    .listSectionSeparator(.hidden, edges: .top)
                }
                .listRowBackground(Color.clear)
                .listRowSeparator(.hidden)
                .listRowInsets(.init())
            }
            .listStyle(.grouped)
            .listRowSpacing(0)
            .scrollContentBackground(.hidden)
            .contentMargins(.top, 16, for: .scrollContent)
            .contentMargins(.horizontal, 12, for: .scrollContent)
            .navigationBarTitleDisplayMode(.inline)
            .animation(.default, value: store.showContactSyncBanner)
        }
    }

    private func creatorsToFollowRow(_ recommendedUser: RecommendUser) -> some View {
        let hasDisplayName = !recommendedUser.user.displayName.isEmpty
        return AddUserRow(
            displayName: hasDisplayName
                ? recommendedUser.user.displayName
                : recommendedUser.user.handle,
            subtitle: recommendedUser.reason.title,
            avatarView: RemoteImage(url: recommendedUser.user.avatarImageUrl, fallbackId: recommendedUser.user.id),
            buttonTitle: recommendedUser.user.isFollowing ? L10n.FeatureSocial.following : L10n.FeatureSocial.follow,
            isLoading: store.followRequestsInFlight.contains(recommendedUser),
            action: { store.send(.followTapped(recommendedUser)) }
        )
    }

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