//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

package import BuildServerIntegration
package import Foundation
@_spi(SourceKitLSP) package import LanguageServerProtocol
@_spi(SourceKitLSP) import LanguageServerProtocolExtensions
package import SemanticIndex
package import SourceKitD

import struct TSCBasic.AbsolutePath
import struct TSCBasic.RelativePath

/// Closures can be used to inspect or modify internal behavior in SourceKit-LSP.
public struct Hooks: Sendable {
  package var indexHooks: IndexHooks

  package var buildServerHooks: BuildServerHooks

  /// A hook that will be executed before a request is handled.
  ///
  /// This allows requests to be artificially delayed.
  package var preHandleRequest: (@Sendable (any RequestType) async -> Void)?

  /// Closure that is executed before a request is forwarded to clangd.
  ///
  /// This allows tests to simulate a `clangd` process that's unresponsive.
  package var preForwardRequestToClangd: (@Sendable (any RequestType) async -> Void)?

  /// If set, called by `SwiftLanguageService` instead of `SourceKitD.getOrCreate` to obtain a
  /// pre-initialized sourcekitd connection, avoiding double-initialization when running in-process.
  /// The URL passed is the toolchain root (`.xctoolchain` bundle path).
  package var sourcekitdCoreInjector: (@Sendable (URL) throws -> (any SourceKitDCore)?)?

  /// Closure that is executed by the background inlay hint refresh task right after it has computed updated hints
  /// but before it writes them into the cache.
  ///
  /// This allows tests to deterministically pause the background refresh and inspect the cache contents written by
  /// the synchronous position-shifting logic before the background task's write can race with it.
  package var inlayHintRefreshWillUpdateCache: (@Sendable () async -> Void)?

  public init() {
    self.init(indexHooks: IndexHooks(), buildServerHooks: BuildServerHooks())
  }

  package init(
    indexHooks: IndexHooks = IndexHooks(),
    buildServerHooks: BuildServerHooks = BuildServerHooks(),
    preHandleRequest: (@Sendable (any RequestType) async -> Void)? = nil,
    preForwardRequestToClangd: (@Sendable (any RequestType) async -> Void)? = nil,
    sourcekitdCoreInjector: (@Sendable (URL) throws -> (any SourceKitDCore)?)? = nil,
    inlayHintRefreshWillUpdateCache: (@Sendable () async -> Void)? = nil
  ) {
    self.indexHooks = indexHooks
    self.buildServerHooks = buildServerHooks
    self.preHandleRequest = preHandleRequest
    self.preForwardRequestToClangd = preForwardRequestToClangd
    self.sourcekitdCoreInjector = sourcekitdCoreInjector
    self.inlayHintRefreshWillUpdateCache = inlayHintRefreshWillUpdateCache
  }
}
