import APIClient
import ComponentLibrary
import ComposableArchitecture
import FeatureClipList
import FeatureToasts
import Localization
import SwiftUI
import Utilities

@Reducer
public struct RemixesList {
    @ObservableState
    public struct State: Equatable {
        @Shared var me: Me
        let parentClip: Clip
        var clipList: ClipList.State
        var loadingState: LoadState { clipList.loadState }

        public init(me: Shared<Me>, parentClip: Clip) {
            self._me = me
            self.parentClip = parentClip
            self.clipList = .init(
                me: me,
                firstPageIndex: 1,
                context: SessionContext(source: .remix(parentClipId: parentClip.id.remoteId))
            )
        }
    }

    public enum Action {
        public enum Delegate {
            case playClipsAt(Clip, [Clip])
        }

        case clipList(ClipList.Action)
        case delegate(Delegate)

        case loadClips
        case dismiss
        case updateClip(Clip)
        case deleteClip(Clip)
    }

    public init() {}

    @Dependency(\.dismiss) private var dismiss
    @Dependency(\.apiClientV2) private var api
    @Dependency(\.eventBus.getOmniplayerChannel) private var getOmniplayerChannel
    @Dependency(\.toastClient.show) private var showToast

    public var body: some ReducerOf<Self> {
        Scope(state: \.clipList, action: \.clipList) {
            ClipList()
        }
        .withClipListClient { state in
            .init(getClips: { page in
                try await api.getDirectChildren(state.parentClip.id, page: page)
            })
        }

        Reduce<State, Action> { _, action in
            switch action {
            case .loadClips:
                return .send(.clipList(.loadClips))

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

            case .updateClip(let clip):
                return .send(.clipList(.updateClip(clip)))

            case .deleteClip(let clip):
                return .send(.clipList(.deleteClip(clip)))

            case .clipList:
                return .none

            case .delegate:
                return .none
            }
        }
    }
}

public struct RemixesListScreen: View {
    @Bindable var store: StoreOf<RemixesList>

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

    public var body: some View {
        ZStack {
            switch store.loadingState {
            case .loading:
                LoadingView()
                    .onAppear { store.send(.loadClips) }

            case .loaded:
                loadedView

            case .failed(let message):
                FailedView(
                    title: L10n.FeatureDiscover.errorTitle,
                    message: message,
                    buttonTitle: L10n.FeatureDiscover.retry,
                    action: { store.send(.loadClips) }
                )
            }
        }
        .background(Color.SemanticV1.backgroundPrimary)
        .navigationBarBackButtonHidden()
        .navigationTitle(Text(L10n.FeatureRemix.remixListTitle(store.parentClip.title)))
        .navigationBarTitleDisplayMode(.large)
        .animation(.default, value: store.loadingState)
    }

    private var loadedView: some View {
        List {
            if store.clipList.clips.isEmpty {
                emptyView
            } else {
                Section {
                    ClipListContent(store: store.scope(state: \.clipList, action: \.clipList)) { _, item in
                        item
                            .padding(.horizontal, 12)
                    }
                }
                .listRowBackground(Color.clear)
                .listSectionSeparator(.hidden, edges: .top)
                .listSectionSpacing(.zero)
                .listRowInsets(.init())
            }
        }
        .background(Color.SemanticV1.backgroundPrimary)
        .listStyle(.plain)
        .stableRefreshable { await store.send(.loadClips).finish() }
    }

    private var emptyView: some View {
        VStack {
            Spacer()
            Text(L10n.FeatureRemix.remixListEmpty)
                .typographyV1(.headline4)
                .foregroundColor(Color.SemanticV1.textPrimary)
                .multilineTextAlignment(.center)
            Spacer()
        }
        .frame(maxWidth: .infinity)
        .frame(height: 100)
        .listRowSeparator(.hidden)
        .listRowBackground(Color.clear)
    }
}
