package com.gensee.multi_instance

import androidx.test.ext.junit.runners.AndroidJUnit4
import com.gensee.media.RtmpPlayer
import org.junit.Test
import org.junit.runner.RunWith
import java.lang.reflect.InvocationTargetException

/**
 * 最小化测试：尝试复现 AbsPlayer 中的 NPE
 *
 * 核心思路：
 * 1. 第一次调用 initAudioPlayer() → 创建 mSynThread 并启动
 * 2. mSynThread 的 onRunStart() 会执行，但因为有 Thread.sleep(100) 延迟
 * 3. 在延迟期间，第二次调用 initAudioPlayer() → 进入 else 分支
 * 4. 此时 mTrack 可能还未赋值，调用 mTrack.getPlayState() → NPE
 */
@RunWith(AndroidJUnit4::class)
class MinimalRaceTest {

    /**
     * 使用反射调用 initAudioPlayer
     */
    private fun initAudioPlayer(player: Any) {
        val method = player.javaClass.superclass.getDeclaredMethod("initAudioPlayer")
        method.isAccessible = true
        method.invoke(player)
    }

    /**
     * 使用反射调用 audioRelease
     */
    private fun audioRelease(player: Any) {
        val method = player.javaClass.superclass.getDeclaredMethod("audioRelease")
        method.isAccessible = true
        method.invoke(player)
    }

    /**
     * 测试场景：连续调用两次 initAudioPlayer()
     *
     * 预期：第二次调用可能进入 else 分支，此时 mTrack 可能还是 null
     */
    @Test
    fun testDoubleInitCall() {
        android.util.Log.i("MinimalRaceTest", "========== Test Started ==========")
        val player = RtmpPlayer()

        try {
            android.util.Log.i("MinimalRaceTest", "First initAudioPlayer() call")
            initAudioPlayer(player)

            // ⚠️ 关键：等待一小段时间，让 mSynThread 启动但 onRunStart 还在执行延迟
            // AbsPlayer.java:364 有 Thread.sleep(100)
            // 我们需要在这 100ms 内发起第二次调用
            android.util.Log.i("MinimalRaceTest", "Waiting 10ms before second call...")
            Thread.sleep(10)

            android.util.Log.i("MinimalRaceTest", "Second initAudioPlayer() call")
            initAudioPlayer(player)

            android.util.Log.i("MinimalRaceTest", "✅ No NPE - test passed")

        } catch (e: InvocationTargetException) {
            val targetException = e.targetException
            if (targetException is NullPointerException) {
                android.util.Log.e("MinimalRaceTest", "🎯🎯🎯 NPE REPRODUCED! 🎯🎯🎯", targetException)
                android.util.Log.e("MinimalRaceTest", "Stack trace:", targetException)

                // 打印详细信息
                targetException.stackTrace.forEach {
                    android.util.Log.e("MinimalRaceTest", "    at $it")
                }

                // 重新抛出以标记测试失败
                throw targetException
            } else {
                android.util.Log.e("MinimalRaceTest", "Other exception", targetException)
                throw targetException
            }
        } catch (e: Exception) {
            android.util.Log.e("MinimalRaceTest", "Unexpected error", e)
            throw RuntimeException(e)
        } finally {
            // 清理
            try {
                android.util.Log.i("MinimalRaceTest", "Cleaning up...")
                Thread.sleep(200)
                audioRelease(player)
                android.util.Log.i("MinimalRaceTest", "Cleanup completed")
            } catch (e: Exception) {
                android.util.Log.e("MinimalRaceTest", "Cleanup failed", e)
            }
        }

        android.util.Log.i("MinimalRaceTest", "========== Test Completed ==========")
    }

    /**
     * 测试场景：多次重复尝试
     *
     * 由于线程调度的不确定性，多次运行可以提高复现概率
     */
    @Test
    fun testRepeatedDoubleInit() {
        val iterations = 20
        var successCount = 0
        var npeCount = 0

        android.util.Log.i("MinimalRaceTest", "========== Repeated Test Started ($iterations iterations) ==========")

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

                // 第一次调用
                initAudioPlayer(player)

                // 短暂等待
                Thread.sleep(10)

                // 第二次调用
                initAudioPlayer(player)

                successCount++
                android.util.Log.d("MinimalRaceTest", "Iteration $iteration: No NPE")

                // 清理
                Thread.sleep(50)
                audioRelease(player)
                Thread.sleep(50)

            } catch (e: InvocationTargetException) {
                if (e.targetException is NullPointerException) {
                    npeCount++
                    android.util.Log.e("MinimalRaceTest", "Iteration $iteration: 🎯 NPE REPRODUCED!")
                }
            } catch (e: Exception) {
                android.util.Log.w("MinimalRaceTest", "Iteration $iteration: Other exception - ${e.message}")
            }
        }

        android.util.Log.i("MinimalRaceTest", "========== Test Results ==========")
        android.util.Log.i("MinimalRaceTest", "Success: $successCount")
        android.util.Log.i("MinimalRaceTest", "NPE: $npeCount")
        android.util.Log.i("MinimalRaceTest", "Total: $iterations")

        if (npeCount > 0) {
            android.util.Log.e("MinimalRaceTest", "🎯 Successfully reproduced NPE $npeCount time(s)!")
        }
    }

    /**
     * 测试场景：快速连续调用（零延迟）
     *
     * 尝试在 mSynThread 完全启动前就发起第二次调用
     */
    @Test
    fun testInstantDoubleInit() {
        android.util.Log.i("MinimalRaceTest", "========== Instant Double Init Test ==========")
        val player = RtmpPlayer()

        try {
            android.util.Log.i("MinimalRaceTest", "First init call")
            initAudioPlayer(player)

            // ⚠️ 零延迟，立即第二次调用
            android.util.Log.i("MinimalRaceTest", "Second init call (instant)")
            initAudioPlayer(player)

            android.util.Log.i("MinimalRaceTest", "✅ No NPE")

        } catch (e: InvocationTargetException) {
            if (e.targetException is NullPointerException) {
                android.util.Log.e("MinimalRaceTest", "🎯 NPE REPRODUCED!", e.targetException)
                throw e.targetException
            }
        } finally {
            try {
                Thread.sleep(200)
                audioRelease(player)
            } catch (e: Exception) {
                android.util.Log.e("MinimalRaceTest", "Cleanup failed", e)
            }
        }
    }

    /**
     * 测试场景：在 else 分支中访问 mTrack
     *
     * 这个测试专门针对 initAudioPlayer() 的 else 分支
     */
    @Test
    fun testElseBranchAccess() {
        android.util.Log.i("MinimalRaceTest", "========== Else Branch Access Test ==========")
        val player = RtmpPlayer()

        try {
            // 第一次调用，创建 mSynThread
            android.util.Log.i("MinimalRaceTest", "First init - creating mSynThread")
            initAudioPlayer(player)

            // 等待足够长的时间，让 mSynThread 完成第一次初始化
            android.util.Log.i("MinimalRaceTest", "Waiting for first init to complete...")
            Thread.sleep(300)

            // 第二次调用，应该进入 else 分支
            android.util.Log.i("MinimalRaceTest", "Second init - should enter else branch")
            initAudioPlayer(player)

            android.util.Log.i("MinimalRaceTest", "✅ No NPE in else branch")

        } catch (e: InvocationTargetException) {
            if (e.targetException is NullPointerException) {
                android.util.Log.e("MinimalRaceTest", "🎯 NPE in else branch!", e.targetException)
                android.util.Log.e("MinimalRaceTest", "This confirms the bug in line 389 of AbsPlayer.java", e.targetException)
                throw e.targetException
            }
        } finally {
            try {
                Thread.sleep(100)
                audioRelease(player)
            } catch (e: Exception) {
                android.util.Log.e("MinimalRaceTest", "Cleanup failed", e)
            }
        }
    }
}
