import AnalyticsClient
import APIClient
import ComponentLibrary
import ComposableArchitecture
import FeatureToasts
import Foundation
import Localization
import SwiftUI
import Utilities

@Reducer
public struct ProfileList {
    @ObservableState
    public struct State: Equatable {
        public var profiles: IdentifiedArrayOf<ProfileListItem.State> = []
        public var loadState: LoadState = .loading
        public var pages: PageState

        var firstLoad = true

        public init(firstPageIndex: Int = 0) {
            self.pages = .init(firstPageIndex: firstPageIndex)
        }

        public func profiles(startingAt profile: ProfileListItem.State) -> [ProfileListItem.State] {
            guard let startIndex = profiles.index(id: profile.id) else { return [] }
            return Array(profiles[startIndex...])
        }

        public func profiles(startingAt profile: ProfileListItem.State) -> [SimpleProfile] {
            profiles(startingAt: profile).map(\.profile)
        }
    }

    public enum Action {
        public enum Internal {
            case profilesLoadResult(page: Int, result: Result<[SimpleProfile], Error>)
        }

        case `internal`(Internal)
        case profiles(IdentifiedActionOf<ProfileListItem>)

        case loadProfiles
        case getNextPage
    }

    @Dependency(ProfileListClient.self) private var profileListClient
    @Dependency(AnalyticsClient.self) private var analytics
    @Dependency(APIClient.self) var apiClient

    public init() {}

    public var body: some ReducerOf<Self> {
        Reduce<State, Action> { state, action in
            switch action {
            case .loadProfiles:
                if state.firstLoad { state.loadState = .loading }
                state.firstLoad = false
                state.pages.reset()

                return .send(.getNextPage)

            case .getNextPage:
                guard let nextPage = state.pages.nextIndex() else { return .none }

                return .run { send in
                    await send(.internal(.profilesLoadResult(page: nextPage, result: Result(catching: { try await profileListClient.getProfiles(nextPage) }))))
                }

            case .internal(.profilesLoadResult(let page, .success(let profiles))):
                if page == state.pages.firstPageIndex {
                    state.profiles = []
                }

                let startIndex = state.profiles.count
                state.profiles.append(contentsOf: profiles.enumerated().map { index, profile in
                    .init(profile: profile, position: (startIndex + index) + 1)
                })
                state.loadState = .loaded
                state.pages.update(profiles)

                return .none

            case .internal(.profilesLoadResult(_, .failure(let error))):
                if state.profiles.isEmpty {
                    state.loadState = .failed(L10n.FeatureClipList.actionFailed)
                }
                state.pages.update(error)
                return .none

            case .profiles, .internal:
                // Catch-all
                return .none
            }
        }
        .forEach(\.profiles, action: \.profiles) {
            ProfileListItem()
        }
    }
}

public struct ProfileListContent<ItemView: View>: View {
    @Bindable private var store: StoreOf<ProfileList>
    private var itemView: (StoreOf<ProfileListItem>, ProfileListItemView) -> ItemView

    public init(store: StoreOf<ProfileList>) where ItemView == ProfileListItemView {
        self.init(store: store, itemView: { $1 })
    }

    public init(store: StoreOf<ProfileList>, @ViewBuilder itemView: @escaping (StoreOf<ProfileListItem>, ProfileListItemView) -> ItemView) {
        self.store = store
        self.itemView = itemView
    }

    public var body: some View {
        ForEach(store.scope(state: \.profiles, action: \.profiles)) { store in
            itemView(store, ProfileListItemView(store: store))
        }

        if store.pages.hasMore, !store.profiles.isEmpty {
            ProgressView()
                .progressViewStyle(.circular)
                .padding()
                .frame(maxWidth: .infinity)
                .listRowSeparator(.hidden)
                .background(Color.clear)
                .listRowBackground(Color.clear)
                .id(UUID())
                .onAppear {
                    guard !store.pages.loadingNext else { return }
                    store.send(.getNextPage)
                }
        }
    }
}
