import Dependencies
import DependenciesMacros
import Foundation

extension DependencyValues {

  /// Holds values defined in the process environment that are needed.
  ///
  /// These are generally loaded from a `.env` file, but also have default values,
  /// if not found.
  public var env: @Sendable () throws -> EnvVars {
    get { self[EnvClient.self].env }
    set { self[EnvClient.self].env = newValue }
  }
}

@DependencyClient
struct EnvClient: Sendable {
  public var env: @Sendable () throws -> EnvVars
}

/// Holds values defined in the process environment that are needed.
///
/// These are generally loaded from a `.env` file, but also have default values,
/// if not found.
public struct EnvVars: Codable, Equatable, Sendable {

  /// The path to the pandoc executable on the system, used to generate pdf's.
  public let pandocPath: String

  /// The pdf engine to use with pandoc when creating pdf's.
  public let pdfEngine: String

  public init(
    pandocPath: String = "/usr/bin/pandoc",
    pdfEngine: String = "weasyprint"
  ) {
    self.pandocPath = pandocPath
    self.pdfEngine = pdfEngine
  }

  enum CodingKeys: String, CodingKey {
    case pandocPath = "PANDOC_PATH"
    case pdfEngine = "PDF_ENGINE"
  }

}

extension EnvClient: DependencyKey {
  static let testValue = Self()

  static let liveValue = Self(env: {
    // Convert default values into a dictionary.
    let defaults =
      (try? encoder.encode(EnvVars()))
      .flatMap { try? decoder.decode([String: String].self, from: $0) }
      ?? [:]

    // Merge the default values with values found in process environment.
    let assigned = defaults.merging(ProcessInfo.processInfo.environment, uniquingKeysWith: { $1 })

    return (try? JSONSerialization.data(withJSONObject: assigned))
      .flatMap { try? decoder.decode(EnvVars.self, from: $0) }
      ?? .init()
  })
}

private let encoder: JSONEncoder = {
  JSONEncoder()
}()

private let decoder: JSONDecoder = {
  JSONDecoder()
}()
