import Combine
import Dependencies
import Foundation
import XCTestDynamicOverlay

public struct HUDManager {
  public struct Show {
    public var run: (HUDModel?) -> Void
    public func callAsFunction(_ model: HUDModel? = nil) {
      run(model)
    }
  }

  public var show: Show
  public var hide: () -> Void
  public var observe: () -> AnyPublisher<HUDModel?, Never>
}

extension HUDManager {
  public static func live() -> HUDManager {
    let subject = PassthroughSubject<HUDModel?, Never>()
    @Dependency(\.mainQueue) var mainQueue
    return HUDManager(
      show: .init { model in
        mainQueue.schedule {
          let model = model ?? HUDModel(hasDotAnimation: true)
          subject.send(model)
          if model.isAutoDismissable {
            mainQueue.schedule(after: mainQueue.now.advanced(by: 2)) {
              subject.send(nil)
            }
          }
        }
      },
      hide: {
        mainQueue.schedule {
          subject.send(nil)
        }
      },
      observe: {
        subject.eraseToAnyPublisher()
      }
    )
  }
}

extension HUDManager {
  public static let unimplemented = HUDManager(
    show: .init(run: XCTestDynamicOverlay.unimplemented("\(Self.self).show")),
    hide: XCTestDynamicOverlay.unimplemented("\(Self.self).hide"),
    observe: XCTestDynamicOverlay.unimplemented(
      "\(Self.self).observe",
      placeholder: Empty().eraseToAnyPublisher()
    )
  )
}

private enum HUDManagerKey: DependencyKey {
  static let liveValue: HUDManager = .live()
  static let testValue: HUDManager = .unimplemented
}

extension DependencyValues {
  public var hudManager: HUDManager {
    get { self[HUDManagerKey.self] }
    set { self[HUDManagerKey.self] = newValue }
  }
}