import APIClient
import ComposableArchitecture
import Utilities

@DependencyClient
public struct ClipLineageClient {
    public var hydrateRelationshipsForClip: (_ clipId: ClipID) -> Void
}

extension ClipLineageClient: DependencyKey {
    public static let liveValue: Self = {
        /// Hydrated by `hydrateRelationshipsForClip`
        @Shared(.inMemory(.clipParentMap)) var clipParentMap: [ClipID: ParentClip] = [:]
        @Shared(.inMemory(.clipDirectChildrenCount)) var clipDirectChildrenCount: [ClipID: Int] = [:]

        func _internal_hydrateClipParentMap(_ clipId: ClipID) async throws {
            /// Check the cache for a valid parent
            /// We don't expect clip parents to change
            if clipParentMap[clipId] != nil {
                return
            }
            /// Fetch the clip parent
            /// `APIClientV2` handles determining whether or not we have a valid clip to fetch here.
            let response = try await APIClientV2.underlying.send(
                Paths.clips.parent.get(clipId: clipId.remoteId)
            )
            let remote = response.value
            let parentClip = try ParentClip(remote)

            /// Update the cache
            $clipParentMap.withLock { $0[clipId] = parentClip }
        }

        func _internal_hydrateClipDirectChildrenCountMap(_ clipId: ClipID) async throws {
            /// This count should be pulled in from `ClipMetadataSchema`, but unfortunately we don't have it there yet so we have to constantly hydrate this...
            let response = try await APIClientV2.underlying.send(
                Paths.clips.directChildrenCount.get(clipId: clipId.remoteId)
            )
            let count = response.value.count

            /// Update the cache
            $clipDirectChildrenCount.withLock { $0[clipId] = count }
        }

        return Self(
            hydrateRelationshipsForClip: { clipId in
                Task {
                    do {
                        try await withThrowingTaskGroup(of: Void.self) { group in
                            /* Get parent */
                            group.addTask { try await _internal_hydrateClipParentMap(clipId) }

                            /* Get child count */
                            group.addTask { try await _internal_hydrateClipDirectChildrenCountMap(clipId) }

                            /* Await for both tasks to complete */
                            try await group.waitForAll()
                        }
                    } catch {
                        log.telemetry.error(error)
                    }
                }
            }
        )
    }()
}

extension ClipLineageClient: TestDependencyKey {
    public static let previewValue = Self()
    public static let testValue = Self()
}

public extension DependencyValues {
    var clipLineageClient: ClipLineageClient {
        get { self[ClipLineageClient.self] }
        set { self[ClipLineageClient.self] = newValue }
    }
}
