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

@Reducer
public struct LikedSongs {
    @ObservableState
    public struct State: Equatable {
        @Shared var me: Me
        @ObservationStateIgnored @ObservedBox var clipList: ClipList.State
        var loadingState: LoadState { clipList.loadState }

        let showBackButton: Bool

        public init(me: Shared<Me>, showBackButton: Bool = true) {
            self._me = me
            self.clipList = .init(me: me, playlist: .liked, firstPageIndex: 1, context: SessionContext(source: .playlist(playlistId: Playlist.liked.id)))
            self.showBackButton = showBackButton
        }
    }

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

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

        case loadClips
        case playClipTapped(ClipListItem.State)
        case dismiss
        case updateClip(Clip)
        case deleteClip(Clip)
    }

    @Dependency(\.dismiss) private var dismiss
    @Dependency(\.apiClientV2) private var api
    @Dependency(NavigationRouterClient.self) private var navigationRouter
    @Dependency(\.toastClient.show) private var showToast

    public init() {}

    public var body: some ReducerOf<Self> {
        Scope(state: \.clipList, action: \.clipList) {
            ClipList()
        }
        .withClipListClient { _ in
            .init(getClips: { page in
                try await api.getPlaylistById(page, Playlist.liked.id, nil, nil).clips
            })
        }

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

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

            // FIXME: Remove after Nav V2
            case .playClipTapped(let clipItem):
                return .send(.delegate(.playClipsAt(clipItem.clip, state.clipList.clips.map(\.clip))))

            case .clipList(.delegate(.toastAfter(let toast))):
                showToast(toast)
                return .none

            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 LikedSongsScreen: View {
    private static let top = "top"

    @Bindable var store: StoreOf<LikedSongs>

    public init(store: StoreOf<LikedSongs>) {
        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.FeaturePlaylistDetail.errorTitle, message: message, buttonTitle: L10n.FeaturePlaylistDetail.retry, action: { store.send(.loadClips) })
            }
        }
        .background(Color.SemanticV1.backgroundPrimary)
        .navigationBarBackButtonHidden()
        .modifier(if: store.showBackButton) {
            $0.customBackButton(background: Color.SemanticV1.backgroundQuaternary) { store.send(.dismiss) }
        }
        .navigationTitle(Text(L10n.FeaturePlaylistDetail.likedSongs))
        .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, 16.0)
                }
                .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.FeaturePlaylistDetail.likedEmptyTitle)
                .typographyV1(.headline4)
                .foregroundColor(Color.SemanticV1.textPrimary)
                .multilineTextAlignment(.center)
            Spacer()
        }
        .frame(maxWidth: .infinity)
        .frame(height: 100)
        .listRowSeparator(.hidden)
        .listRowBackground(Color.clear)
    }
}
