//
// Copyright (c) Double Symmetry GmbH
// Commercial use requires a license. See https://rntp.dev/pricing
//

import UIKit

private let imageCache = NSCache<NSString, UIImage>()

struct MediaItem {
  let mediaId: String
  let url: MediaURL
  let title: String?
  let artist: String?
  let albumTitle: String?
  let artworkUrl: MediaURL?
  let duration: Double?
  let isLiveStream: Bool?
  let extras: [String: Any]?

  init(mediaId: String, url: MediaURL, title: String?, artist: String?, albumTitle: String?, artworkUrl: MediaURL?, duration: Double? = nil, isLiveStream: Bool? = nil, extras: [String: Any]? = nil) {
    self.mediaId = mediaId
    self.url = url
    self.title = title
    self.artist = artist
    self.albumTitle = albumTitle
    self.artworkUrl = artworkUrl
    self.duration = duration
    self.isLiveStream = isLiveStream
    self.extras = extras
  }

  init(data: [String: Any]) {
    guard let url = MediaURL(object: data["url"]) else { fatalError("url is a required property") }

    let urlString = url.isLocal ? url.value.path : url.value.absoluteString
    let mediaId = (data["mediaId"] as? String) ?? urlString

    let title = data["title"] as? String
    let artist = data["artist"] as? String
    let albumTitle = data["albumTitle"] as? String
    let artworkUrl = MediaURL(object: data["artworkUrl"])
    let duration = data["duration"] as? Double
    let isLiveStream = data["isLive"] as? Bool
    let extras = data["extras"] as? [String: Any]

    self.init(mediaId: mediaId, url: url, title: title, artist: artist, albumTitle: albumTitle, artworkUrl: artworkUrl, duration: duration, isLiveStream: isLiveStream, extras: extras)
  }
}

extension MediaItem {
  func withMetadata(
    title: String?? = nil,
    artist: String?? = nil,
    albumTitle: String?? = nil,
    artworkUrl: MediaURL?? = nil
  ) -> MediaItem {
    MediaItem(
      mediaId: self.mediaId,
      url: self.url,
      title: title ?? self.title,
      artist: artist ?? self.artist,
      albumTitle: albumTitle ?? self.albumTitle,
      artworkUrl: artworkUrl ?? self.artworkUrl,
      duration: self.duration,
      isLiveStream: self.isLiveStream,
      extras: self.extras
    )
  }

  func toDictionary() -> [String: Any] {
    var dict: [String: Any] = [
      "mediaId": mediaId,
      "url": url.isLocal ? url.value.path : url.value.absoluteString
    ]
    if let title = title { dict["title"] = title }
    if let artist = artist { dict["artist"] = artist }
    if let albumTitle = albumTitle { dict["albumTitle"] = albumTitle }
    if let artworkUrl = artworkUrl {
      dict["artworkUrl"] = artworkUrl.isLocal ? artworkUrl.value.path : artworkUrl.value.absoluteString
    }
    if let duration = duration { dict["duration"] = duration }
    if let isLiveStream = isLiveStream { dict["isLive"] = isLiveStream }
    if let extras = extras { dict["extras"] = extras }
    return dict
  }
}

extension MediaItem: AudioItem {
  var sourceUrl: String { url.isLocal ? url.value.path : url.value.absoluteString }
  var sourceType: SourceType { url.isLocal ? .file : .stream }
  var headers: [String: String]? { url.headers }
  var isLive: Bool { self.isLiveStream ?? false }

  func getArtwork(_ handler: @escaping (UIImage?) -> Void) {
    guard let artworkUrl = artworkUrl?.value else {
      handler(nil)
      return
    }

    let cacheKey = artworkUrl.absoluteString as NSString
    if let cached = imageCache.object(forKey: cacheKey) {
      handler(cached)
      return
    }

    if self.artworkUrl!.isLocal {
      if let image = UIImage(contentsOfFile: artworkUrl.path) {
        imageCache.setObject(image, forKey: cacheKey)
        handler(image)
      } else {
        handler(nil)
      }
    } else {
      URLSession.shared.dataTask(with: artworkUrl) { data, response, error in
        guard let data = data, error == nil, let artwork = UIImage(data: data) else {
          handler(nil)
          return
        }
        imageCache.setObject(artwork, forKey: cacheKey)
        handler(artwork)
      }.resume()
    }
  }
}
