import APIClient
import ComponentLibrary
import ComposableArchitecture
import EventBusClient
import FeatureBrandedAlert
import FeatureToasts
import Foundation
import Localization
import SnippetPlayerClient

@Reducer
public struct HooksPostScreenReducer {
    @Reducer(state: .equatable)
    public enum Destination {
        case brandedAlert(BrandedAlert)
        case fullscreenPreview(FullscreenPreview)
        case publishSongPopUp
    }

    @ObservableState
    public struct State: Equatable {
        @Presents public var destination: Destination.State?
        @Shared var me: Me
        public var caption: String = ""
        public var clipId: ClipID
        public var clipStartTime: Double
        public var clipEndTime: Double
        public var sourceVideoStartTime: Double
        public var sourceVideoEndTime: Double
        public var localVideoURL: URL?
        public var videoVolumeLevel: Float
        public var snippetVolumeLevel: Float
        public var showLyrics: Bool = false
        public var showLyricsTooltip: Bool = false
        public var allowComments: Bool = true
        public var clip: Clip?
        public var isLoadingClip: Bool = false
        public var isLoadingPreviewData: Bool = false
        public var isLoadingForPreviewTap: Bool = false
        public var isVideoPlaying: Bool = true
        public var videoRestartTrigger: Int = 0
        public var isPublishingSong: Bool = false
        public var toast: ToastReducer.State.ToastType?

        public init(
            caption: String = "",
            clipId: ClipID,
            clipStartTime: Double,
            clipEndTime: Double,
            sourceVideoStartTime: Double,
            sourceVideoEndTime: Double,
            localVideoURL: URL? = nil,
            videoVolumeLevel: Float,
            snippetVolumeLevel: Float,
            me: Shared<Me>
        ) {
            self.caption = caption
            self.clipId = clipId
            self.clipStartTime = clipStartTime
            self.clipEndTime = clipEndTime
            self.sourceVideoStartTime = sourceVideoStartTime
            self.sourceVideoEndTime = sourceVideoEndTime
            self.localVideoURL = localVideoURL
            self.videoVolumeLevel = videoVolumeLevel
            self.snippetVolumeLevel = snippetVolumeLevel
            self.showLyrics = snippetVolumeLevel > 0
            self._me = me
        }
    }

    public enum Action: BindableAction {
        case binding(BindingAction<State>)
        case destination(PresentationAction<Destination.Action>)
        case task
        case backTapped
        case doneTapped
        case confirmGoBack
        case downloadHookTapped
        case captionEdited(String)
        case moreOptionsTapped
        case showLyricsTapped
        case showLyricsToggled(Bool)
        case showLyricsTooltipTapped
        case allowCommentsToggled(Bool)
        case postHookTapped
        case previewTapped
        case playPreview
        case publishClipTapped
        case publishClipResponse(Result<Void, Error>)
        case dismissPublishClipPopUp
        case setVideoPlaying(Bool)
        case fetchClip
        case fetchClipResponse(Result<Clip, Error>)

        case delegate(Delegate)

        public enum Delegate {
            case backTapped
            case postTapped(
                clipId: String,
                clipStartTime: Double,
                clipEndTime: Double,
                sourceVideoStartTime: Double,
                sourceVideoEndTime: Double,
                caption: String,
                videoVolumeLevel: Float,
                snippetVolumeLevel: Float,
                showLyrics: Bool,
                allowComments: Bool
            )
        }
    }

    @Dependency(\.apiClientV2) var apiV2
    @Dependency(\.eventBus.sendClipEvent) private var sendClipEvent

    public init() {}

    public var body: some ReducerOf<Self> {
        BindingReducer()
        Reduce { state, action in
            switch action {
            case .binding:
                return .none

            case .task:
                return .send(.fetchClip)

            case .backTapped:
                if state.caption.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
                    return .send(.delegate(.backTapped))
                } else {
                    state.destination = .brandedAlert(.init(style: .multiButtonAlert(.v2(.custom(.init(
                        title: L10n.FeatureHooks.goBackTitle,
                        description: L10n.FeatureHooks.goBackDescription,
                        primaryButtonLabel: L10n.FeatureHooks.goBack,
                        secondaryButtonLabel: L10n.FeatureHooks.cancel
                    ))))))
                    return .none
                }

            case .doneTapped:
                return .none

            case .confirmGoBack:
                return .send(.delegate(.backTapped))

            case .downloadHookTapped:
                return .none

            case let .captionEdited(caption):
                state.caption = caption
                return .none

            case .moreOptionsTapped:
                return .none

            case .showLyricsTapped:
                return .none

            case let .showLyricsToggled(showLyrics):
                state.showLyrics = showLyrics
                return .none

            case .showLyricsTooltipTapped:
                state.showLyricsTooltip.toggle()
                return .none

            case let .allowCommentsToggled(allowComments):
                state.allowComments = allowComments
                return .none

            case .postHookTapped:
                guard let clip = state.clip else { return .none }
                if !clip.isPublic {
                    state.destination = .publishSongPopUp
                    return .none
                } else {
                    return .send(.delegate(.postTapped(
                        clipId: state.clipId.remoteId,
                        clipStartTime: state.clipStartTime,
                        clipEndTime: state.clipEndTime,
                        sourceVideoStartTime: state.sourceVideoStartTime,
                        sourceVideoEndTime: state.sourceVideoEndTime,
                        caption: state.caption,
                        videoVolumeLevel: state.videoVolumeLevel,
                        snippetVolumeLevel: state.snippetVolumeLevel,
                        showLyrics: state.showLyrics,
                        allowComments: state.allowComments
                    )))
                }

            case .publishClipTapped:
                if let clip = state.clip {
                    state.isPublishingSong = true
                    return .run { send in
                        await send(.publishClipResponse(Result(catching: { try await apiV2.setVisibility(clip, true) })))
                    }
                } else {
                    return .none
                }

            case .publishClipResponse(.success(_)):
                state.isPublishingSong = false
                state.destination = nil
                state.toast = ToastReducer.State.ToastType.success(
                    L10n.FeatureHooks.publishSongSuccessMessage,
                    position: .top
                )
                if var updatedClip = state.clip {
                    updatedClip.isPublic = true
                    state.clip = updatedClip
                    sendClipEvent(.updateClip(updatedClip))
                }
                return .send(.fetchClip)

            case .publishClipResponse(.failure(let error)):
                state.isPublishingSong = false
                state.toast = ToastReducer.State.ToastType.warning(
                    L10n.FeatureHooks.somethingWentWrong,
                    position: .top
                )
                return .none

            case .dismissPublishClipPopUp:
                state.destination = nil
                return .none

            case .previewTapped:
                if let clip = state.clip {
                    state.isVideoPlaying = false
                    state.destination = .fullscreenPreview(
                        FullscreenPreview.State(
                            clipId: state.clipId,
                            clipStartTime: state.clipStartTime,
                            clipEndTime: state.clipEndTime,
                            caption: state.caption.isEmpty ? nil : state.caption,
                            clip: clip,
                            localVideoURL: state.localVideoURL,
                            videoVolumeLevel: state.videoVolumeLevel,
                            snippetVolumeLevel: state.snippetVolumeLevel,
                            user: state.me.user
                        )
                    )
                    return .none
                } else {
                    state.isLoadingForPreviewTap = true
                    return .send(.fetchClip)
                }

            case .destination(.presented(.brandedAlert(.destination(.presented(.multiButtonAlert(.delegate(.tappedPrimaryButton))))))):
                return .send(.confirmGoBack)

            case .destination(.presented(.brandedAlert(.destination(.presented(.multiButtonAlert(.delegate(.tappedSecondaryButton))))))):
                return .none

            case .destination(.presented(.fullscreenPreview(.closeTapped))):
                state.destination = nil
                state.isVideoPlaying = true
                state.videoRestartTrigger += 1
                return .none

            case .fetchClip:
                state.isLoadingPreviewData = true
                return .run { [clipId = state.clipId] send in
                    await send(.fetchClipResponse(Result(catching: {
                        try await apiV2.getClip(clipId.remoteId)
                    })))
                }

            case .fetchClipResponse(.success(let clip)):
                state.isLoadingPreviewData = false
                state.clip = clip

                // If loading was initiated by preview tap, open fullscreen immediately
                if state.isLoadingForPreviewTap {
                    state.isLoadingForPreviewTap = false
                    state.isVideoPlaying = false
                    state.destination = .fullscreenPreview(
                        FullscreenPreview.State(
                            clipId: state.clipId,
                            clipStartTime: state.clipStartTime,
                            clipEndTime: state.clipEndTime,
                            caption: state.caption.isEmpty ? nil : state.caption,
                            clip: clip,
                            localVideoURL: state.localVideoURL,
                            videoVolumeLevel: state.videoVolumeLevel,
                            snippetVolumeLevel: state.snippetVolumeLevel,
                            user: state.me.user
                        )
                    )
                }
                return .none

            case .fetchClipResponse(.failure(let error)):
                state.isLoadingPreviewData = false
                state.isLoadingForPreviewTap = false
                state.toast = ToastReducer.State.ToastType.warning(
                    L10n.FeatureHooks.somethingWentWrong,
                    position: .top
                )
                return .none

            case .playPreview:
                state.isVideoPlaying = true
                return .none

            case let .setVideoPlaying(isPlaying):
                state.isVideoPlaying = isPlaying
                return .none

            case .destination:
                return .none

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