/*
 * 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.analysis.low.level.api.fir.transformers

import org.jetbrains.kotlin.analysis.low.level.api.fir.api.targets.LLFirResolveTarget
import org.jetbrains.kotlin.analysis.low.level.api.fir.util.checkExpectForActualIsResolved
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.fir.FirElementWithResolveState
import org.jetbrains.kotlin.fir.canHaveDeferredReturnTypeCalculation
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.languageVersionSettings
import org.jetbrains.kotlin.fir.resolve.transformers.mpp.FirExpectActualMatcherTransformer
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase

internal object LLFirExpectActualMatcherLazyResolver : LLFirLazyResolver(FirResolvePhase.EXPECT_ACTUAL_MATCHING) {
    override fun createTargetResolver(target: LLFirResolveTarget): LLFirTargetResolver = LLFirExpectActualMatchingTargetResolver(target)

    override fun phaseSpecificCheckIsResolved(target: FirElementWithResolveState) {
        if (target.moduleData.session.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects) &&
            target is FirMemberDeclaration &&
            target.canHaveExpectCounterPart()
        ) {
            checkExpectForActualIsResolved(target)
        }
    }
}

/**
 * This resolver is responsible for [EXPECT_ACTUAL_MATCHING][FirResolvePhase.EXPECT_ACTUAL_MATCHING] phase.
 *
 * This resolver:
 * - Searches and set [expectForActual][org.jetbrains.kotlin.fir.declarations.expectForActual]
 *   property for declarations.
 *
 * Special rules:
 * - First resolves outer classes to this phase in multiplatform projects.
 *
 * @see FirExpectActualMatcherTransformer
 * @see FirResolvePhase.EXPECT_ACTUAL_MATCHING
 */
private class LLFirExpectActualMatchingTargetResolver(target: LLFirResolveTarget) : LLFirTargetResolver(
    target,
    FirResolvePhase.EXPECT_ACTUAL_MATCHING
) {
    private val enabled = resolveTargetSession.languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects)

    @Deprecated("Should never be called directly, only for override purposes, please use withRegularClass", level = DeprecationLevel.ERROR)
    override fun withContainingRegularClass(firClass: FirRegularClass, action: () -> Unit) {
        if (enabled) {
            // Resolve outer classes before resolving inner declarations. It's the requirement of FirExpectActualResolver
            firClass.lazyResolveToPhase(resolverPhase.previous)
            performResolve(firClass)
        }
        action()
    }

    private val transformer = object : FirExpectActualMatcherTransformer(resolveTargetSession, resolveTargetScopeSession) {
        override fun transformRegularClass(regularClass: FirRegularClass, data: Nothing?): FirStatement {
            transformMemberDeclaration(regularClass)
            return regularClass
        }
    }

    override fun doLazyResolveUnderLock(target: FirElementWithResolveState) {
        if (enabled && target is FirMemberDeclaration && target.canHaveExpectCounterPart()) {
            transformer.transformMemberDeclaration(target)
        }
    }
}

private fun FirMemberDeclaration.canHaveExpectCounterPart(): Boolean = when (this) {
    // We shouldn't try to calculate expect/actual mapping for fake declarations
    is FirCallableDeclaration if canHaveDeferredReturnTypeCalculation -> false
    is FirEnumEntry -> true
    is FirProperty -> true
    is FirConstructor -> true
    is FirSimpleFunction -> true
    is FirRegularClass -> true
    is FirTypeAlias -> true
    else -> false
}
