/*
 * Copyright 2025 Sergey S. Chernov real.sergeych@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package net.sergeych.lyng

import net.sergeych.lyng.obj.Obj
import net.sergeych.lyng.obj.ObjInt
import java.io.File
import kotlin.system.measureNanoTime
import kotlin.test.Test
import kotlin.test.Ignore

/**
 * Baseline range iteration benchmark. It measures for-loops over integer ranges under
 * current implementation and records timings. When RANGE_FAST_ITER is implemented,
 * this test will also serve for OFF vs ON A/B.
 */
@Ignore("TODO(compile-time-res): legacy tests disabled")
class RangeIterationBenchmarkTest {

    private fun outFile(): File = File("lynglib/build/range_iter_bench.txt")

    private fun writeHeader(f: File) {
        if (!f.parentFile.exists()) f.parentFile.mkdirs()
        f.writeText("[DEBUG_LOG] Range iteration benchmark results\n")
    }

    private fun appendLine(f: File, s: String) { f.appendText(s + "\n") }

    private suspend fun buildSumScriptInclusive(n: Int, iters: Int): Script {
        // Sum 0..n repeatedly to stress iteration
        val src = """
            var total = 0
            for (k in 0..${iters - 1}) {
                var s = 0
                for (i in 0..$n) { s += i }
                total += s
            }
            total
        """.trimIndent()
        return Compiler.compile(Source("<range-inc>", src), Script.defaultImportManager)
    }

    private suspend fun buildSumScriptExclusive(n: Int, iters: Int): Script {
        val src = """
            var total = 0
            for (k in 0..${iters - 1}) {
                var s = 0
                for (i in 0..<$n) { s += i }
                total += s
            }
            total
        """.trimIndent()
        return Compiler.compile(Source("<range-exc>", src), Script.defaultImportManager)
    }

    private suspend fun buildSumScriptReversed(n: Int, iters: Int): Script {
        val src = """
            var total = 0
            for (k in 0..${iters - 1}) {
                var s = 0
                // reversed-like loop using countdown range (n..0)
                for (i in $n..0) { s += i }
                total += s
            }
            total
        """.trimIndent()
        return Compiler.compile(Source("<range-rev>", src), Script.defaultImportManager)
    }

    private suspend fun buildSumScriptNegative(n: Int, iters: Int): Script {
        // Sum -n..n repeatedly
        val src = """
            var total = 0
            for (k in 0..${iters - 1}) {
                var s = 0
                for (i in -$n..$n) { s += (i < 0 ? -i : i) }
                total += s
            }
            total
        """.trimIndent()
        return Compiler.compile(Source("<range-neg>", src), Script.defaultImportManager)
    }

    private suspend fun buildSumScriptEmpty(iters: Int): Script {
        // Empty range 1..0 should not iterate
        val src = """
            var total = 0
            for (k in 0..${iters - 1}) {
                var s = 0
                for (i in 1..0) { s += 1 }
                total += s
            }
            total
        """.trimIndent()
        return Compiler.compile(Source("<range-empty>", src), Script.defaultImportManager)
    }

    private suspend fun runOnce(script: Script): Long {
        val scope = Script.newScope()
        var result: Obj? = null
        val t = measureNanoTime { result = script.execute(scope) }
        if (result !is ObjInt) println("[DEBUG_LOG] result=${result?.javaClass?.simpleName}")
        return t
    }

    @Test
    fun bench_range_iteration_baseline() = runTestBlocking {
        val f = outFile()
        writeHeader(f)

        val savedFlag = PerfFlags.RANGE_FAST_ITER
        try {
            val n = 1000
            val iters = 500

            // Baseline with current flag (OFF by default)
            PerfFlags.RANGE_FAST_ITER = false
            val sIncOff = buildSumScriptInclusive(n, iters)
            val tIncOff = runOnce(sIncOff)
            val sExcOff = buildSumScriptExclusive(n, iters)
            val tExcOff = runOnce(sExcOff)
            appendLine(f, "[DEBUG_LOG] OFF inclusive=${tIncOff} ns, exclusive=${tExcOff} ns")

            // Also record ON times
            PerfFlags.RANGE_FAST_ITER = true
            val sIncOn = buildSumScriptInclusive(n, iters)
            val tIncOn = runOnce(sIncOn)
            val sExcOn = buildSumScriptExclusive(n, iters)
            val tExcOn = runOnce(sExcOn)
            appendLine(f, "[DEBUG_LOG] ON  inclusive=${tIncOn} ns, exclusive=${tExcOn} ns")

            // Additional scenarios: reversed, negative, empty
            PerfFlags.RANGE_FAST_ITER = false
            val sRevOff = buildSumScriptReversed(n, iters)
            val tRevOff = runOnce(sRevOff)
            val sNegOff = buildSumScriptNegative(n, iters)
            val tNegOff = runOnce(sNegOff)
            val sEmptyOff = buildSumScriptEmpty(iters)
            val tEmptyOff = runOnce(sEmptyOff)
            appendLine(f, "[DEBUG_LOG] OFF reversed=${tRevOff} ns, negative=${tNegOff} ns, empty=${tEmptyOff} ns")

            PerfFlags.RANGE_FAST_ITER = true
            val sRevOn = buildSumScriptReversed(n, iters)
            val tRevOn = runOnce(sRevOn)
            val sNegOn = buildSumScriptNegative(n, iters)
            val tNegOn = runOnce(sNegOn)
            val sEmptyOn = buildSumScriptEmpty(iters)
            val tEmptyOn = runOnce(sEmptyOn)
            appendLine(f, "[DEBUG_LOG] ON  reversed=${tRevOn} ns, negative=${tNegOn} ns, empty=${tEmptyOn} ns")
        } finally {
            PerfFlags.RANGE_FAST_ITER = savedFlag
        }
    }
}

private fun runTestBlocking(block: suspend () -> Unit) {
    kotlinx.coroutines.runBlocking { block() }
}
