import APIClient
import CommentsClient
import ComponentLibrary
import ComposableArchitecture
import Errors
import EventBusClient
import FeatureBrandedAlert
import GenAPI
import Localization
import SongActionsClient
import SwiftUI
import Utilities

@Reducer
public struct MoreOptionsReducer {
    public typealias State = MoreOptionsState

    public enum Action: BindableAction {
        case task
        case binding(BindingAction<State>)
        case updateAllowComments(Bool)
        case updateAllowDownloads(Bool)
        case updateAllowRemixing(Bool)
        case togglePinToProfile(Bool)
        case clearError
        case `internal`(Internal)
        case dismissTapped
        case delegate(Delegate)
        case brandedAlert(BrandedAlert.Action)
        case toggleShowVideoCoverInHooksFeed

        public enum Internal {
            case dismiss
            case toggleRemixabilityResponse(Result<Clip, Error>)
            case getPinnedClips
            case getPinnedClipsResponse(Result<PinnedClipsContainer, Error>)
        }

        public enum Delegate {
            case clipPinningUpdated
        }
    }

    @Dependency(\.apiClientV2) var api
    @Dependency(\.telemetryClient) var telemetry
    @Dependency(\.dismiss) var dismiss
    @Dependency(\.commentsClient) var commentsClient
    @Dependency(\.eventBus.sendClipEvent) private var sendClipEvent
    @Dependency(SongActionsClient.self) var songActionsClient

    public init() {}

    public var body: some ReducerOf<Self> {
        BindingReducer()
        Scope(state: \.brandedAlert, action: \.brandedAlert) {
            BrandedAlert()
        }
        Reduce<State, Action> { state, action in
            switch action {
            case .task:
                return .send(.internal(.getPinnedClips))

            case .binding:
                return .none

            case .clearError:
                state.error = nil
                return .none

            case .dismissTapped:
                return .send(.internal(.dismiss))

            case .updateAllowComments(let allowComments):
                state.commentsToggle = .updating(allowComments)
                commentsClient.enqueueSetCommentsAccessOnClip(state.clip.id, allowComments)
                // Don't sendClipEvent here because comments access is handled by
                // the Shared commentsAccessMap
                state.commentsToggle = .idle(allowComments)
                state.toggleHapticSuccess.toggle()
                return .none

            case .updateAllowDownloads(let allowDownloads):
                state.downloadsToggle = .updating(allowDownloads)
                // Add network request once we add this option
                state.downloadsToggle = .idle(allowDownloads)
                state.toggleHapticSuccess.toggle()
                return .none

            case .updateAllowRemixing(let allowRemixing):
                state.remixingToggle = .updating(allowRemixing)
                state.toggleHapticSuccess.toggle()
                return .run { [clip = state.clip] send in
                    var newClip = clip
                    newClip.canRemix = allowRemixing
                    let apiResult = await Result { try await api.toggleRemixability(clip.id, enabled: allowRemixing) }
                        .map { newClip }
                    await send(.internal(.toggleRemixabilityResponse(apiResult)))
                }

            case .internal(.toggleRemixabilityResponse(.success(let clip))):
                state.remixingToggle = .idle(clip.canRemix)
                state.clip.canRemix = clip.canRemix
                sendClipEvent(.updateClip(clip))
                return .none

            case .internal(.toggleRemixabilityResponse(.failure(let error))):
                /// Error alert
                state.error = error.erasedToAnyError()
                /// Doesn't do anything right now but feels right
                state.remixingToggle = .error(error.erasedToAnyError())
                log.telemetry.error(error)
                return .none

            case .togglePinToProfile(let updatedPinToProfile):
                guard case .idle(let pinToProfile) = state.pinningToggle else { return .none }
                state.toggleHapticSuccess.toggle()
                if updatedPinToProfile != pinToProfile,
                   updatedPinToProfile == true,
                   state.pinnedClipsList.count >= 5,
                   !state.hasConfirmedPinLimitReplace
                {
                    return .send(.brandedAlert(.setStyle(.multiButtonAlert(.preset(.pinningWillClearOldestPin)))))
                }

                state.pinningToggle = .idle(updatedPinToProfile)
                return .send(.delegate(.clipPinningUpdated))

            case .internal(.getPinnedClips):
                return .run { send in
                    await send(.internal(.getPinnedClipsResponse(
                        Result(catching: {
                            try await api.getPinnedClips()
                        })
                    )))
                }

            case .internal(.getPinnedClipsResponse(let action)):
                switch action {
                case .success(let clipResponse):
                    state.pinnedClipsList = clipResponse.pinnedClips
                case .failure(let error):
                    log.telemetry.error(error)
                }
                return .none

            case .toggleShowVideoCoverInHooksFeed:
                let originalClip = state.clip

                state.clip.optOutVideoCoverHook.toggle()
                state.videoCoverInHooksFeedToggle = .idle(!state.clip.optOutVideoCoverHook)
                state.toggleHapticSuccess.toggle()

                songActionsClient.toggleVideoCoverInHooksFeed(
                    clip: originalClip,
                    showVideoCover: originalClip.optOutVideoCoverHook
                )
                return .none

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

            case .brandedAlert(.destination(.presented(.multiButtonAlert(.delegate(.tappedPrimaryButton))))):
                state.hasConfirmedPinLimitReplace = true
                return .none

            case .brandedAlert(.destination(.presented(.multiButtonAlert(.delegate(.tappedSecondaryButton))))):
                state.hasConfirmedPinLimitReplace = false
                return .none

            case .delegate, .brandedAlert:
                return .none
            }
        }
    }
}
