#pragma once

#include <audioapi/events/EventCaller.hpp>
#include <audioapi/utils/Macros.h>
#include <audioapi/utils/Result.hpp>
#include <audioapi/utils/SpscChannel.hpp>
#include <atomic>
#include <memory>
#include <string>
#include <tuple>

#ifdef __APPLE__
typedef const struct AudioBufferList *AudioDataType;
#else
typedef void *AudioDataType;
#endif

namespace audioapi {

class AudioFileProperties;
class AudioEventHandlerRegistry;

using OpenFileResult = Result<std::string, std::string>;
using CloseFileResult = Result<std::tuple<double, double>, std::string>;

class AudioFileWriter {
 public:
  DELETE_COPY_AND_MOVE(AudioFileWriter);

  AudioFileWriter(
      const std::shared_ptr<AudioEventHandlerRegistry> &audioEventHandlerRegistry,
      const std::shared_ptr<AudioFileProperties> &fileProperties);
  virtual ~AudioFileWriter() = default;

  virtual CloseFileResult closeFile() = 0;
  virtual std::string getFilePath() const = 0;

  virtual void writeAudioData(AudioDataType data, int numFrames) = 0;

  virtual double getCurrentDuration() const = 0;
  virtual size_t getFileSizeBytes() const = 0;

  void setOnErrorCallback(uint64_t callbackId) {
    assignOnErrorCallbackId(callbackId);
  }
  void clearOnErrorCallback() {
    assignOnErrorCallbackId(0);
  }
  void assignOnErrorCallbackId(uint64_t callbackId);
  void invokeOnErrorCallback(const std::string &message);

 protected:
  bool isFileOpen();

  std::atomic<bool> isFileOpen_{false};
  std::atomic<size_t> framesWritten_{0};
  EventCaller<AudioEvent::RECORDER_ERROR> errorEvent_;

  std::shared_ptr<AudioFileProperties> fileProperties_;

  static constexpr auto FILE_WRITER_SPSC_OVERFLOW_STRATEGY =
      channels::spsc::OverflowStrategy::OVERWRITE_ON_FULL;
  static constexpr auto FILE_WRITER_SPSC_WAIT_STRATEGY = channels::spsc::WaitStrategy::ATOMIC_WAIT;
  static constexpr size_t FILE_WRITER_POOL_SIZE = 32;
  // SPSC rings hold at most (capacity - 1) elements.
  static constexpr auto FILE_WRITER_CHANNEL_CAPACITY = FILE_WRITER_POOL_SIZE + 1;
  // At most POOL_SIZE slots can be in flight at once, so sizing the channel one
  // larger guarantees the ring is never full when a slot is available — which is
  // why the producer can use the blocking send() without it ever actually waiting.
  static_assert(
      FILE_WRITER_POOL_SIZE <= FILE_WRITER_CHANNEL_CAPACITY - 1,
      "Channel must hold every in-flight slot so send() never blocks/overwrites");
};

} // namespace audioapi
