import Foundation
import CommonCrypto

enum AvatarCache {

  /// Default max cache size in bytes (read from Info.plist, fallback 50MB).
  static var maxCacheSize: Int {
    let megabytes = Bundle.main.object(forInfoDictionaryKey: "CacheSizeLimitMB") as? Int ?? 50
    return megabytes * 1024 * 1024
  }

  /// Remove old cached files for a given id (files with a different URL hash).
  static func removeOldFiles(for id: String, in directory: URL) {
    guard let files = try? FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) else { return }
    for file in files where file.lastPathComponent.hasPrefix("\(id)_") {
      try? FileManager.default.removeItem(at: file)
    }
  }

  /// Find any cached file for a given id (used for stale fallback).
  static func findAnyFile(for id: String, in directory: URL) -> URL? {
    guard let files = try? FileManager.default.contentsOfDirectory(at: directory, includingPropertiesForKeys: nil) else { return nil }
    return files.first { $0.lastPathComponent.hasPrefix("\(id)_") }
  }

  /// Evict oldest files if total cache size exceeds the limit.
  static func evictIfNeeded(in directory: URL) {
    let fm = FileManager.default
    guard let files = try? fm.contentsOfDirectory(
      at: directory,
      includingPropertiesForKeys: [.contentAccessDateKey, .fileSizeKey]
    ) else { return }

    var totalSize: Int = 0
    var fileInfos: [(url: URL, accessDate: Date, size: Int)] = []

    for file in files {
      guard let values = try? file.resourceValues(forKeys: [.contentAccessDateKey, .fileSizeKey]),
            let size = values.fileSize else { continue }
      let accessDate = values.contentAccessDate ?? Date.distantPast
      totalSize += size
      fileInfos.append((url: file, accessDate: accessDate, size: size))
    }

    guard totalSize > maxCacheSize else { return }

    // Sort by access date ascending (oldest first)
    fileInfos.sort { $0.accessDate < $1.accessDate }

    for info in fileInfos {
      guard totalSize > maxCacheSize else { break }
      try? fm.removeItem(at: info.url)
      totalSize -= info.size
    }
  }

  /// Simple MD5 hash of a string, returns hex string.
  static func md5Hash(_ string: String) -> String {
    let data = Data(string.utf8)
    var hash = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
    data.withUnsafeBytes { bytes in
      _ = CC_MD5(bytes.baseAddress, CC_LONG(data.count), &hash)
    }
    return hash.map { String(format: "%02x", $0) }.joined()
  }
}
