//
//  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)

  private var chunkIndex: UInt64 = 0

  init(outputURL: URL, onChunkReady: @escaping ((Chunk) -> Void)) throws {
    self.outputURL = outputURL
    self.onChunkReady = onChunkReady
    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")
    save(data: data, url: url)
    onChunkReady(url: url, type: .initialization)
  }

  private func saveSegment(_ data: Data, report: AVAssetSegmentReport?) {
    let name = "\(chunkIndex).mp4"
    let url = outputURL.appendingPathComponent(name)
    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) {
    do {
      try data.write(to: url)
    } catch {
      ReactLogger.log(level: .error, message: "Unable to write \(url): \(error.localizedDescription)")
    }
  }

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

}
