package com.gensee.multi_instance

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.gensee.media.RtmpPlayer
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger

/**
 * 测试 AbsPlayer 中的 AudioTrack 并发竞态条件
 *
 * 复现场景：
 * 1. Thread A 在 onRunStart() 中创建 AudioTrack (mTrack = new AudioTrack())
 * 2. Thread B 同时调用 audioRelease() → onRunEnd() → mTrack = null
 * 3. Thread A 继续执行，调用 mTrack.getPlayState() 时 NPE
 *
 * 注意：由于 initAudioPlayer() 和 audioRelease() 是 protected 方法，
 * 本测试使用反射来调用这些方法。
 */
@RunWith(AndroidJUnit4::class)
class AudioTrackRaceConditionTest {

    /**
     * 使用反射调用 protected 的 initAudioPlayer() 方法
     */
    private fun initAudioPlayerReflective(player: Any) {
        try {
            val method = player.javaClass.superclass.getDeclaredMethod("initAudioPlayer")
            method.isAccessible = true
            method.invoke(player)
        } catch (e: Exception) {
            android.util.Log.e("RaceConditionTest", "Failed to call initAudioPlayer", e)
            throw RuntimeException("Reflection failed for initAudioPlayer", e)
        }
    }

    /**
     * 使用反射调用 protected 的 audioRelease() 方法
     */
    private fun audioReleaseReflective(player: Any) {
        try {
            val method = player.javaClass.superclass.getDeclaredMethod("audioRelease")
            method.isAccessible = true
            method.invoke(player)
        } catch (e: Exception) {
            android.util.Log.e("RaceConditionTest", "Failed to call audioRelease", e)
            throw RuntimeException("Reflection failed for audioRelease", e)
        }
    }

    /**
     * 基础测试：确保 RtmpPlayer 可以正常初始化和释放
     */
    @Test
    fun testBasicPlayerInit() {
        val player = RtmpPlayer()

        try {
            initAudioPlayerReflective(player)
            android.util.Log.i("RaceConditionTest", "Basic init succeeded")

            Thread.sleep(200)

            audioReleaseReflective(player)
            android.util.Log.i("RaceConditionTest", "Basic release succeeded")
        } catch (e: Exception) {
            android.util.Log.e("RaceConditionTest", "Basic test failed", e)
            fail("Basic test should not throw exception: ${e.message}")
        }
    }

    /**
     * 简化版测试：不使用 CountDownLatch，依赖 Thread.sleep() 调节时序
     */
    @Test
    fun testSimpleConcurrentAccess() {
        val context = InstrumentationRegistry.getInstrumentation().targetContext
        var crashCount = 0

        repeat(20) { iteration ->
            try {
                val player = RtmpPlayer()

                // 快速连续调用
                val initThread = Thread {
                    try {
                        initAudioPlayerReflective(player)
                    } catch (e: Exception) {
                        android.util.Log.e("RaceConditionTest", "Init failed in iteration $iteration", e)
                    }
                }

                val releaseThread = Thread {
                    try {
                        Thread.sleep(5)
                        audioReleaseReflective(player)
                    } catch (e: Exception) {
                        android.util.Log.e("RaceConditionTest", "Release failed in iteration $iteration", e)
                    }
                }

                initThread.start()
                releaseThread.start()

                initThread.join(1000)
                releaseThread.join(1000)

                // 清理
                try {
                    audioReleaseReflective(player)
                } catch (e: Exception) {
                    // ignore
                }
            } catch (e: NullPointerException) {
                crashCount++
                android.util.Log.e("RaceConditionTest", "NPE caught in iteration $iteration", e)
            }
        }

        android.util.Log.i("RaceConditionTest", "Simple test completed. Crashes: $crashCount/20")
    }

    /**
     * 测试快速连续的 init/release 是否会触发 NPE
     *
     * 这个测试通过并发执行 initAudioPlayer() 和 audioRelease()
     * 来增加竞态条件触发的概率
     */
    @Test
    fun testConcurrentInitAndRelease() {
        val context = InstrumentationRegistry.getInstrumentation().targetContext

        // 重复多次以增加竞态窗口
        val iterations = 50
        val crashCount = AtomicInteger(0)

        repeat(iterations) { iteration ->
            try {
                val player = RtmpPlayer()
                val latch1 = CountDownLatch(1) // 确保 init 开始
                val latch2 = CountDownLatch(1) // 确保 release 执行

                // Thread 1: 执行 initAudioPlayer
                val initThread = Thread {
                    try {
                        initAudioPlayerReflective(player)
                        latch1.countDown()
                        latch2.await(500, TimeUnit.MILLISECONDS)
                    } catch (e: Exception) {
                        android.util.Log.e("RaceConditionTest",
                            "Iteration $iteration initThread exception", e)
                    }
                }

                // Thread 2: 等待 init 后立即释放
                val releaseThread = Thread {
                    try {
                        latch1.await(1, TimeUnit.SECONDS)
                        audioReleaseReflective(player)
                        latch2.countDown()
                    } catch (e: Exception) {
                        android.util.Log.e("RaceConditionTest",
                            "Iteration $iteration releaseThread exception", e)
                    }
                }

                initThread.start()
                releaseThread.start()

                initThread.join(2000)
                releaseThread.join(2000)

                // 清理
                try {
                    audioReleaseReflective(player)
                } catch (e: Exception) {
                    // ignore
                }

            } catch (e: NullPointerException) {
                // 如果捕获到 NPE，说明竞态条件被触发
                crashCount.incrementAndGet()
                android.util.Log.e("RaceConditionTest",
                    "NPE caught in iteration $iteration", e)
            } catch (e: Exception) {
                android.util.Log.e("RaceConditionTest",
                    "Other exception in iteration $iteration", e)
            }
        }

        android.util.Log.i("RaceConditionTest",
            "Test completed. Crashes: ${crashCount.get()}/$iterations")

        // 注意：这个测试不能保证每次都触发竞态条件
        // 如果没有崩溃，说明：1) 修复了 2) 竞态窗口没命中 3) 需要增加延迟
    }
}
