//
//  ChunkedRecorder.swift
//  VisionCamera
//
//  Created by Rafael Bastos on 12/07/2024.
//  Copyright © 2024 mrousavy. All rights reserved.
//

import Foundation
import AVFoundation


class ChunkedRecorder: NSObject {

  enum ChunkType {
    case initialization
    case data(index: UInt64, duration: CMTime?)
  }

  struct Chunk {
    let url: URL
    let type: ChunkType
  }

  let outputURL: URL
  let onChunkReady: ((Chunk) -> Void)
  let onError: ((Error) -> Void)?

  private var chunkIndex: UInt64 = 0

  init(outputURL: URL, onChunkReady: @escaping ((Chunk) -> Void), onError: ((Error) -> Void)? = nil) throws {
    self.outputURL = outputURL
    self.onChunkReady = onChunkReady
    self.onError = onError
    guard FileManager.default.fileExists(atPath: outputURL.path) else {
      throw CameraError.unknown(message: "output directory does not exist at: \(outputURL.path)", cause: nil)
    }
  }

}

extension ChunkedRecorder: AVAssetWriterDelegate {

  func assetWriter(_ writer: AVAssetWriter,
                   didOutputSegmentData segmentData: Data,
                   segmentType: AVAssetSegmentType,
                   segmentReport: AVAssetSegmentReport?) {

    switch segmentType {
    case .initialization:
      saveInitSegment(segmentData)
    case .separable:
      saveSegment(segmentData, report: segmentReport)
    @unknown default:
      fatalError("Unknown AVAssetSegmentType!")
    }
  }

  private func saveInitSegment(_ data: Data) {
    let url = outputURL.appendingPathComponent("init.mp4")
    do {
      try data.write(to: url)
      onChunkReady(url: url, type: .initialization)
    } catch {
      ReactLogger.log(level: .error, message: "Failed to write init file \(url): \(error.localizedDescription)")
      onError?(CameraError.capture(.fileError))
    }
  }

  private func saveSegment(_ data: Data, report: AVAssetSegmentReport?) {
    let name = "\(chunkIndex).mp4"
    let url = outputURL.appendingPathComponent(name)
    if save(data: data, url: url) {
      let duration = report?
        .trackReports
        .filter { $0.mediaType == .video }
        .first?
        .duration
      onChunkReady(url: url, type: .data(index: chunkIndex, duration: duration))
      chunkIndex += 1
    }
  }

  private func save(data: Data, url: URL) -> Bool {
    do {
      try data.write(to: url)
      return true
    } catch {
      ReactLogger.log(level: .error, message: "Unable to write \(url): \(error.localizedDescription)")
      return false
    }
  }

  private func onChunkReady(url: URL, type: ChunkType) {
    onChunkReady(Chunk(url: url, type: type))
  }

}
