import AnalyticsClient
import APIClient
import ComposableArchitecture
import FeatureShareAssetCreation
import FeatureToasts
import Foundation
import LyricsClient
import ShareAssetClient
import StatsigClient
import Waveform

@Reducer
public struct ShareSheetReducer<Item: Shareable> {
    public typealias State = ShareSheetState<Item>

    @Reducer(state: .equatable)
    public enum Destination {
        case shareSheet(ShareSheetReducer<Clip>)
        case assetCreation(AssetCreationReducer)
        case messageComposer
        case systemShareSheet
        case downloadScreen(AssetDownloadScreenReducer)
    }

    @CasePathable
    public enum Action: BindableAction {
        case binding(BindingAction<State>)
        case destination(PresentationAction<Destination.Action>)
        case share(ShareReducer<Item>.Action)
        case shareLink(ShareLinkReducer<Item>.Action)
        case lyricsEvent(LyricsClientV2.Event)

        case subscribeToLyricsEvents
        case dismissDestination
        case dismissedAssetCreationSheet
        case onAppear
    }

    @Dependency(APIClient.self) var apiClient
    @Dependency(\.apiClientV2) var apiClientV2
    @Dependency(AnalyticsClient.self) var analyticsClient
    @Dependency(\.continuousClock) private var clock
    @Dependency(LyricsClientV2.self) var lyricsClient
    @Dependency(\.lyricsClientV2.stream) var lyricsEventStream

    public init() {}

    public var body: some ReducerOf<Self> {
        BindingReducer()

        Scope(state: \.self, action: \.share) {
            ShareReducer<Item>()
        }

        Scope(state: \.self, action: \.shareLink) {
            ShareLinkReducer<Item>()
        }

        Reduce { state, action in
            switch action {
            case .onAppear:
                return .concatenate(
                    .send(.subscribeToLyricsEvents),
                    .send(.shareLink(.loadAttribution))
                )

            case .subscribeToLyricsEvents:
                guard let clip = state.itemAsClip else { return .none }
                return .stream(
                    lyricsClient.streamForClip(clip),
                    send: Action.lyricsEvent,
                    cancellableId: ShareSheetLyricsEventStreamCancellable()
                )

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

            case .dismissedAssetCreationSheet:
                state.assetCreationPlayer.stop()
                return .none

            case .share(.triggerShareActionTarget(let shareAction)):
                guard let clip = state.itemAsClip else { return .none }
                analyticsClient.track(.audioAction(.shareSongTapped(shareType: shareAction.rawValue), clip: clip))

                // Platform-specific sharing is handled by ShareReducer
                let platformName: String? = switch shareAction {
                case .platform(let platform): platform.rawValue
                case .system: nil
                }
                let shareId = state.shareLinkAttribution.value??.shareId
                let incrementEffect = Effect<Action>.run { [clipId = clip.id] _ in
                    do {
                        try await apiClientV2.incrementClipActionCount(clipId, .share, platformName, shareId)
                    } catch {
                        log.telemetry.error(error, message: "Failed to increment clip action count.")
                    }
                }

                switch shareAction {
                case .platform(let targetPlatform):
                    state.setLastUsed(platform: targetPlatform)
                    return .merge(
                        incrementEffect,
                        .send(.shareLink(.registerAttribution(targetPlatform)))
                    )

                case .system:
                    return incrementEffect
                }

            case .destination(.presented(.assetCreation(.delegate(
                .startCreationWithConfig(let config, let assetTarget, let triggerID, let startTime, let endTime)
            )))):
                guard let itemAsClip = state.itemAsClip else { return .none }
                state.destination = nil
                state.destination = .downloadScreen(.init(
                    clip: itemAsClip,
                    triggerID: triggerID,
                    shareURL: state.effectiveShareURL,
                    assetTarget: assetTarget,
                    presetConfig: config,
                    lyricsData: state.lyricsData ?? .empty,
                    startTime: startTime,
                    endTime: endTime
                ))
                return .none

            case .lyricsEvent(let event):
                switch event {
                case .didStartPollingForAlignedLyrics:
                    break

                case .didPollClipForAlignedLyrics:
                    break

                case .didSucceedPollingLyrics(let clip, let lyricsData, let waveformData):
                    if let currentClip = state.itemAsClip,
                       clip.id == currentClip.id,
                       let clipAsItem = clip as? Item
                    {
                        state.item = clipAsItem
                        state.lyricsData = lyricsData
                        state.waveformData = waveformData
                    }

                case .didFailPolling(_, let error):
                    log.telemetry.error(error)
                }
                return .none

            case .share:
                return .none

            case .binding:
                return .none

            case .destination:
                return .none

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

        Analytics()
    }
}

extension ShareSheetState {
    var availableActions: [ShareableTarget] {
        var actions = item.shareActions

        if item is Hook {
            let canDownloadHook = FeatureFlag.hooks.allowAllHookDownloads || (FeatureFlag.hooks.allowSelfHookDownload && isCreator == true)
            if !canDownloadHook {
                actions.remove(.system(.download))
            }
        }

        return actions.sorted { lhs, rhs in
            sortIndex(for: lhs) < sortIndex(for: rhs)
        }
    }

    /// Maintains the platforms last recently usage order
    /// - Parameter platform: the Platform the user tapped on.
    fileprivate mutating func setLastUsed(platform: Platform) {
        $platformsLRUOrder.withLock { lruOrder in
            if let index = lruOrder.firstIndex(of: platform) {
                lruOrder.remove(at: index)
            }
            lruOrder.insert(platform, at: 0)
        }
    }

    private func sortIndex(for action: ShareableTarget) -> Int {
        func index(of platform: Platform) -> Int {
            platformsLRUOrder.firstIndex(of: platform) ?? platformsLRUOrder.count
        }
        switch action {
        case .system(.copyLink): return 0
        case .system(.download): return 1
        case .platform(let platform): return 2 + index(of: platform)
        case .system(.more): return 2 + Platform.allCases.count
        }
    }
}

/* Can't but this inside generic struct reducer */
struct ShareSheetLyricsEventStreamCancellable: Hashable {}
