import Dependencies
import DependenciesMacros
import Foundation
import Vapor

extension DependencyValues {
  /// Dependency used for file operations.
  public var fileClient: FileClient {
    get { self[FileClient.self] }
    set { self[FileClient.self] = newValue }
  }
}

@DependencyClient
public struct FileClient: Sendable {
  public typealias OnCompleteHandler = @Sendable () async throws -> Void

  /// Write contents to a file.
  ///
  /// > Warning: This will overwrite a file if it exists.
  public var writeFile: @Sendable (_ contents: String, _ path: String) async throws -> Void
  /// Remove a file.
  public var removeFile: @Sendable (_ path: String) async throws -> Void
  /// Stream a file.
  public var streamFile:
    @Sendable (_ path: String, @escaping OnCompleteHandler) async throws -> Response

  /// Stream a file at the given path.
  ///
  /// - Paramters:
  ///   - path: The path to the file to stream.
  ///   - onComplete: Completion handler to run when done streaming the file.
  public func streamFile(
    at path: String,
    onComplete: @escaping OnCompleteHandler = {}
  ) async throws -> Response {
    try await streamFile(path, onComplete)
  }
}

extension FileClient: TestDependencyKey {
  public static let testValue = Self()

  public static func live(fileIO: FileIO) -> Self {
    .init(
      writeFile: { contents, path in
        try await fileIO.writeFile(ByteBuffer(string: contents), at: path)
      },
      removeFile: { path in
        try FileManager.default.removeItem(atPath: path)
      },
      streamFile: { path, onComplete in
        try await fileIO.asyncStreamFile(at: path) { _ in
          try await onComplete()
        }
      }
    )
  }
}
