import APIClient
import ComponentLibrary
import ComposableArchitecture
import EventBusClient
import FeatureHooksGrid
import FeatureHooksModels
import FeatureHooksPost
import Localization
import UIKit
import Utilities

@Reducer
public struct MyHooksReducer {
    @ObservableState
    public struct State: Equatable {
        public enum Tab: CaseIterable {
            case madeByMe
            case liked
            var title: String {
                switch self {
                case .madeByMe:
                    return L10n.FeatureHooksGrid.madeByMe
                case .liked:
                    return L10n.FeatureHooksGrid.liked
                }
            }
        }

        let showBackButton: Bool
        var selectedTab: Tab = .madeByMe
        var myHooksGrid: HooksGridReducer.State
        var likedHooksGrid: HooksGridReducer.State

        public init(
            showBackButton: Bool,
            hooksGrid: HooksGridReducer.State,
            likedHooksInitialState: HooksGridConfig.InitialState = .none
        ) {
            self.showBackButton = showBackButton
            self.myHooksGrid = hooksGrid
            let likedConfig = HooksGridConfig.likedHooks(
                me: hooksGrid.$me,
                initialState: likedHooksInitialState
            )
            self.likedHooksGrid = HooksGridReducer.State(dependency: likedConfig)
        }
    }

    public enum Action {
        case task
        case myHooksGrid(HooksGridReducer.Action)
        case likedHooksGrid(HooksGridReducer.Action)
        case hookEvent(EventBusClient.HookEvent)
        case hooksPostClientEvent(FeatureHooksPost.HooksPostClient.HooksPostClientEvent)
        case checkInFlightTasks
        case removePendingIfNeeded(hooks: [Hook] = [], uploadId: String? = nil)
        case setSelectedTab(State.Tab)
        case backButtonTapped
    }

    @Dependency(\.apiClientV2) var api
    @Dependency(\.eventBus.getHookPublisher) var getHookPublisher
    @Dependency(\.hooksPostClient) var hooksPostClient
    public init() {}
    public var body: some ReducerOf<Self> {
        Scope(state: \.myHooksGrid, action: \.myHooksGrid) {
            HooksGridReducer()
                .withHooksGridClient { state in
                    HooksGridClient(
                        getHooks: { page in
                            let fetchedHooks = try await api.getUserCreatedHooks(page, state.pageSize)
                            let hooksInGrid = fetchedHooks.userDisplayableHooks
                            return hooksInGrid
                        }
                    )
                }
        }
        Scope(state: \.likedHooksGrid, action: \.likedHooksGrid) {
            HooksGridReducer()
                .withHooksGridClient { state in
                    HooksGridClient(
                        getHooks: { page in
                            try await api.getUserLikedHooks(page, state.pageSize)
                        }
                    )
                }
        }
        Reduce { state, action in
            struct HooksPostClientEventObserver: Hashable {}

            switch action {
            case .task:
                struct HookEventCancellableId: Hashable {}

                return .merge(
                    .stream(
                        hooksPostClient.events(),
                        send: Action.hooksPostClientEvent,
                        cancellableId: HooksPostClientEventObserver()
                    ),
                    .subscribe(getHookPublisher(), send: Action.hookEvent, cancellableId: HookEventCancellableId()),
                    .send(.checkInFlightTasks),
                    .send(.myHooksGrid(.task))
                )

            case .checkInFlightTasks:
                return .merge(
                    .send(.myHooksGrid(.internal(.startCheckingForPendingHooks))),
                    .run { send in
                        if let latest = await hooksPostClient.fetchMostRecentTask() {
                            await send(.hooksPostClientEvent(.statusChanged(
                                taskId: latest.id,
                                status: latest.status,
                                thumbnailImage: latest.thumbnailImage,
                                videoUploadId: latest.uploadId
                            )))
                        }
                        await send(.myHooksGrid(.internal(.finishCheckingForPendingHooks)))
                    }
                )

            case let .myHooksGrid(.delegate(.hooksLoaded(hooks, isFirstPage: true))):
                if let firstHook = hooks.first {
                    return .send(.removePendingIfNeeded(hooks: [firstHook]))
                }
                return .none

            case let .myHooksGrid(.delegate(.hooksLoaded(hooks, isFirstPage: false))):
                return .send(.removePendingIfNeeded(hooks: hooks))

            case .hookEvent(let event):
                switch event {
                case .hookCreated(let hook), .hookUpdated(let hook):
                    state.myHooksGrid.pendingHooks.removeAll(where: { $0.hook.id == "pending-hooks-post" })
                    return .send(.myHooksGrid(.hookEvent(event)))

                case .triggeredHookCreation(let hook):
                    return .send(.myHooksGrid(.hookEvent(event)))

                case .hookLiked(let hook):
                    if let index = state.myHooksGrid.hooks.firstIndex(where: { $0.id == hook.id }) {
                        var updatedHook = state.myHooksGrid.hooks[index]
                        state.myHooksGrid.hooks[index] = updatedHook
                    }

                    if !state.likedHooksGrid.hooks.contains(where: { $0.id == hook.id }) {
                        state.likedHooksGrid.hooks.insert(hook, at: 0)
                    }
                    return .none

                case .hookUnliked(let hookId):
                    if let index = state.myHooksGrid.hooks.firstIndex(where: { $0.id == hookId }) {
                        var updatedHook = state.myHooksGrid.hooks[index]
                        updatedHook.currentUserLiked = false
                        state.myHooksGrid.hooks[index] = updatedHook
                    }

                    state.likedHooksGrid.hooks.removeAll(where: { $0.id == hookId })
                    return .none

                case .hookDeleted:
                    state.myHooksGrid.pendingHooks.removeAll(where: { $0.hook.id == "pending-hooks-post" })
                    return .merge(
                        .send(.myHooksGrid(.hookEvent(event))),
                        .send(.likedHooksGrid(.hookEvent(event)))
                    )

                default:
                    return .none
                }

            case let .hooksPostClientEvent(.statusChanged(_, status, thumbnailImage, videoUploadId)):
                // Smart fallback: If pending hooks were prefetched from Library, skip creation
                // If navigated from Profile/elsewhere, create pending hooks from HooksPostClient
                let shouldCreatePending = state.myHooksGrid.pendingHooks.isEmpty

                if shouldCreatePending {
                    // Not navigated from Library - create pending hooks from HooksPostClient
                    switch status {
                    case .uploadVideoFailed, .createHookFailed, .hookProcessingFailed:
                        // Remove pending hook on failures
                        state.myHooksGrid.pendingHooks.removeAll(where: { $0.hook.id == "pending-hooks-post" })
                        return .none

                    default:
                        // Create or update pending Hook for non-failure statuses
                        let pendingHook = status.toPendingHook(videoUploadId: videoUploadId)
                        let pending = HooksGridConfig.PendingHook(hook: pendingHook, thumbnail: thumbnailImage)
                        if let idx = state.myHooksGrid.pendingHooks.firstIndex(where: { $0.hook.id == pending.hook.id }) {
                            state.myHooksGrid.pendingHooks[idx] = pending
                        } else {
                            state.myHooksGrid.pendingHooks.insert(pending, at: 0)
                        }
                    }
                }
                return .none

            case .hooksPostClientEvent(.cancelled):
                // Clear pending hook when hook post is cancelled
                state.myHooksGrid.pendingHooks.removeAll(where: { $0.hook.id == "pending-hooks-post" })
                return .none

            case .hooksPostClientEvent:
                return .none

            case .myHooksGrid:
                return .none

            case .likedHooksGrid:
                return .none

            case let .removePendingIfNeeded(hooks, uploadId):
                guard let pending = state.myHooksGrid.pendingHooks.first,
                      let pendingIds = pending.hook.videoUploadIds,
                      !pendingIds.isEmpty
                else {
                    return .none
                }

                var shouldRemove = false

                // Check if any provided hooks match the pending
                if !hooks.isEmpty {
                    shouldRemove = hooks.contains { hook in
                        guard let ids = hook.videoUploadIds, !ids.isEmpty else { return false }
                        return ids.contains(where: { pendingIds.contains($0) })
                    }
                }

                // Check if the uploadId matches any existing hooks
                if !shouldRemove, let uploadId = uploadId {
                    shouldRemove = state.myHooksGrid.hooks.contains { hook in
                        (hook.videoUploadIds ?? []).contains(uploadId)
                    }
                }

                if shouldRemove {
                    state.myHooksGrid.pendingHooks.removeAll()
                }
                return .none

            case .setSelectedTab(let tab):
                guard state.selectedTab != tab else { return .none }
                state.selectedTab = tab

                // Load data for the tab if empty
                switch tab {
                case .madeByMe:
                    if state.myHooksGrid.hooks.isEmpty {
                        return .send(.myHooksGrid(.task))
                    }

                case .liked:
                    if state.likedHooksGrid.hooks.isEmpty {
                        return .send(.likedHooksGrid(.task))
                    }
                }
                return .none

            case .backButtonTapped:
                switch state.selectedTab {
                case .madeByMe:
                    return .send(.myHooksGrid(.dismiss))
                case .liked:
                    return .send(.likedHooksGrid(.dismiss))
                }
            }
        }
    }
}

