
import APIClient
import ComposableArchitecture
import FeatureInAppNotifications
import InAppNotificationClient
import SwiftUI

@Reducer
public struct DemoInAppNotifications {
    @ObservableState
    public struct State: Equatable {
        var inAppNotificationsList = InAppNotificationList.State()

        @Shared(.inMemory(.inAppNotificationsState)) var inAppNotificationsState: NotificationLoadingState = .loading
        @Shared(.inMemory(.inAppNotificationMap)) var inAppNotificationMap: OrderedNotificationMap = .defaultValue
        @Shared(.inMemory(.hasUnreadNotifications)) var hasUnreadNotifications: Bool = false

        public init() {
            self.setupNotificationsList()
        }

        public mutating func setupNotificationsList() {
            guard let mockResponse = loadMockNotificationItem() else { return }
            let mockNotifications: [InAppNotificationItem] = mockResponse
                .notifications.map { .v2($0) }

            let map = InAppNotificationProcessing
                .groupedNotifications(mockNotifications)

            $inAppNotificationMap.withLock { $0 = map }
        }

        func loadMockNotificationItem() -> UserNotificationV2? {
            guard let url = Bundle.main.url(forResource: "mock_response", withExtension: "json") else {
                print("Failed to locate mock_response.json in bundle.")
                return nil
            }

            do {
                let data = try Data(contentsOf: url)
                let decoder = JSONDecoder()
                // decoder.keyDecodingStrategy = .convertFromSnakeCase
                decoder.dateDecodingStrategy = .custom { decoder -> Date in
                    let container = try decoder.singleValueContainer()
                    let dateStr = try container.decode(String.self)

                    if let date = DateFormatter.iso8601Full.date(from: dateStr) {
                        return date
                    } else {
                        throw DecodingError.dataCorruptedError(in: container,
                                                               debugDescription: "Expected date string to be ISO8601-formatted with milliseconds")
                    }
                }
                let item = try decoder.decode(UserNotificationV2.self, from: data)
                return item
            } catch {
                print("Failed to decode: \(error)")
                return nil
            }
        }
    }

    public enum Action {
        case task
        case onAppear
        case dismiss
        case inAppNotificationsList(InAppNotificationList.Action)
    }

    public init() {}

    public var body: some ReducerOf<Self> {
        Scope(state: \.inAppNotificationsList, action: \.inAppNotificationsList, child: {
            InAppNotificationList()
        })

        Reduce<State, Action> { _, action in
            switch action {
            case .onAppear:
                return .none
            case .inAppNotificationsList:
                return .none
            case .task:
                return .none
            case .dismiss:
                return .none
            }
        }
    }
}

public struct DemoInAppNotificationsView: View {
    let store: StoreOf<DemoInAppNotifications>
    @Environment(\.scenePhase) var scenePhase

    public init(store: StoreOf<DemoInAppNotifications>) {
        self.store = store
    }

    public var body: some View {
        VStack(spacing: .zero) {
            DemoTitleHeaderBar(
                title: "In App Notifications",
                onBackAction: {
                    store.send(.dismiss)
                }
            )
            ZStack {
                ScrollView {
                    let store = store.scope(
                        state: \.inAppNotificationsList,
                        action: \.inAppNotificationsList
                    )
                    InAppNotificationListView(store: store)
                }
            }
            .padding(.horizontal, 8.0)
        }
        .task {
            store.send(.task)
        }
        .onAppear {
            store.send(.onAppear)
        }
    }
}
