/*
 * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.kapt.test.handlers

import com.sun.tools.javac.tree.JCTree
import com.sun.tools.javac.util.List
import org.jetbrains.kotlin.kapt.KaptContextForStubGeneration
import org.jetbrains.kotlin.kapt.base.parseJavaFiles
import org.jetbrains.kotlin.kapt.javac.KaptJavaFileObject
import org.jetbrains.kotlin.kapt.stubs.KaptStubConverter
import org.jetbrains.kotlin.kapt.util.prettyPrint
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.getRealJavaFiles
import org.jetbrains.kotlin.test.services.sourceFileProvider
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import java.io.File

abstract class BaseKaptHandler(testServices: TestServices) : AbstractKaptHandler(testServices) {
    protected fun convert(
        module: TestModule,
        kaptContext: KaptContextForStubGeneration,
        generateNonExistentClass: Boolean
    ): List<JCTree.JCCompilationUnit> {
        val javaFiles = testServices.sourceFileProvider.getRealJavaFiles(module)
        val converter = KaptStubConverter(kaptContext, generateNonExistentClass)

        val kaptStubs = converter.convert()
        val convertedFiles = kaptStubs.mapIndexed { index, stub ->
            val sourceFile = module.createTempJavaFile("stub$index.java", stub.file.prettyPrint(kaptContext.context))
            stub.writeMetadataIfNeeded(forSource = sourceFile)
            sourceFile
        }

        val allJavaFiles = javaFiles + convertedFiles

        // A workaround needed for Javac to parse files correctly even if errors were already reported
        // If nerrors > 0, "parseFiles()" returns the empty list
        val oldErrorCount = kaptContext.compiler.log.nerrors
        kaptContext.compiler.log.nerrors = 0

        try {
            val parsedJavaFiles = kaptContext.parseJavaFiles(allJavaFiles)

            for (tree in parsedJavaFiles) {
                val actualFile = File(tree.sourceFile.toUri())

                // By default, JavaFileObject.getName() returns the absolute path to the file.
                // In our test, such a path will be temporary, so the comparison against it will lead to flaky tests.
                tree.sourcefile = KaptJavaFileObject(tree, tree.defs.firstIsInstance(), actualFile)
            }

            return parsedJavaFiles
        } finally {
            kaptContext.compiler.log.nerrors = oldErrorCount
        }
    }

    private fun TestModule.createTempJavaFile(name: String, text: String): File {
        return testServices.sourceFileProvider.getJavaSourceDirectoryForModule(this).resolve(name).also {
            it.writeText(text)
        }
    }
}
