/*
 * 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.sir.providers

import org.jetbrains.kotlin.analysis.api.KaImplementationDetail
import org.jetbrains.kotlin.analysis.api.KaSession
import org.jetbrains.kotlin.analysis.api.analyze
import org.jetbrains.kotlin.analysis.api.projectStructure.KaModule
import org.jetbrains.kotlin.analysis.api.scopes.KaScope
import org.jetbrains.kotlin.analysis.api.symbols.KaDeclarationSymbol
import org.jetbrains.kotlin.analysis.api.types.KaType
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.sir.*
import org.jetbrains.kotlin.sir.providers.impl.BridgeProvider.BridgeFunctionProxy
import org.jetbrains.kotlin.sir.providers.impl.StandaloneSirTypeNamer


/**
 * A single entry point for all facilities that are required for running Analysis API -> SIR translation.
 *
 * Similar classes:
 * 1. [KaSession][org.jetbrains.kotlin.analysis.api.KaSession] from Analysis API.
 * 2. [FirSession][org.jetbrains.kotlin.fir.FirSession] from K2.
 */
public interface SirSession :
    SirDeclarationNamer,
    SirDeclarationProvider,
    SirParentProvider,
    SirTrampolineDeclarationsProvider,
    SirModuleProvider,
    SirTypeProvider,
    SirVisibilityChecker,
    SirChildrenProvider,
    SirBridgeProvider
{
    public val sirSession: SirSession
        get() = this

    public val useSiteModule: KaModule

    public val enumGenerator: SirEnumGenerator

    public val declarationNamer: SirDeclarationNamer
    public val declarationProvider: SirDeclarationProvider
    public val parentProvider: SirParentProvider
    public val trampolineDeclarationsProvider: SirTrampolineDeclarationsProvider
    public val moduleProvider: SirModuleProvider
    public val typeProvider: SirTypeProvider
    public val visibilityChecker: SirVisibilityChecker
    public val childrenProvider: SirChildrenProvider
    public val bridgeProvider: SirBridgeProvider

    override val errorTypeStrategy: SirTypeProvider.ErrorTypeStrategy
        get() = typeProvider.errorTypeStrategy
    override val unsupportedTypeStrategy: SirTypeProvider.ErrorTypeStrategy
        get() = typeProvider.unsupportedTypeStrategy

    override fun KaDeclarationSymbol.sirDeclarationName(): String = with(declarationNamer) { this@sirDeclarationName.sirDeclarationName() }

    override fun KaDeclarationSymbol.toSir(): SirTranslationResult = with(declarationProvider) { this@toSir.toSir() }

    override fun KaDeclarationSymbol.getSirParent(ktAnalysisSession: KaSession): SirDeclarationParent =
        with(parentProvider) { this@getSirParent.getSirParent(ktAnalysisSession) }

    override fun KaDeclarationSymbol.getOriginalSirParent(ktAnalysisSession: KaSession): SirElement =
        with(parentProvider) { this@getOriginalSirParent.getOriginalSirParent(ktAnalysisSession) }

    override fun SirDeclaration.trampolineDeclarations(): List<SirDeclaration> = with (trampolineDeclarationsProvider) {
        this@trampolineDeclarations.trampolineDeclarations()
    }

    override fun KaModule.sirModule(): SirModule = with(moduleProvider) { this@sirModule.sirModule() }

    override fun KaType.translateType(
        ktAnalysisSession: KaSession,
        reportErrorType: (String) -> Nothing,
        reportUnsupportedType: () -> Nothing,
        processTypeImports: (List<SirImport>) -> Unit,
    ): SirType =
        with(typeProvider) {
            this@translateType.translateType(
                ktAnalysisSession,
                reportErrorType,
                reportUnsupportedType,
                processTypeImports
            )
        }

    @Deprecated("Use this.sirAvailability instead", ReplaceWith("this.sirAvailability(ktAnalysisSession)"))
    @Suppress("DEPRECATION")
    override fun KaDeclarationSymbol.sirVisibility(ktAnalysisSession: KaSession): SirVisibility? =
        with(visibilityChecker) { this@sirVisibility.sirVisibility(ktAnalysisSession) }

    override fun KaDeclarationSymbol.sirAvailability(ktAnalysisSession: KaSession): SirAvailability =
        with(visibilityChecker) { this@sirAvailability.sirAvailability(ktAnalysisSession) }

    override fun Sequence<KaDeclarationSymbol>.extractDeclarations(kaSession: KaSession): Sequence<SirDeclaration> =
        with(childrenProvider) { this@extractDeclarations.extractDeclarations(kaSession) }

    override fun generateFunctionBridge(
        baseBridgeName: String,
        explicitParameters: List<SirParameter>,
        returnType: SirType,
        kotlinFqName: List<String>,
        selfParameter: SirParameter?,
        extensionReceiverParameter: SirParameter?,
        errorParameter: SirParameter?
    ): BridgeFunctionProxy? = with(bridgeProvider) {
        generateFunctionBridge(
            baseBridgeName,
            explicitParameters,
            returnType,
            kotlinFqName,
            selfParameter,
            extensionReceiverParameter,
            errorParameter
        )
    }

    override fun generateTypeBridge(
        kotlinFqName: List<String>,
        swiftFqName: String,
        swiftSymbolName: String,
    ): SirTypeBindingBridge? = with(bridgeProvider) {
        generateTypeBridge(kotlinFqName, swiftFqName, swiftSymbolName)
    }
}

/**
 * Provides methods to create [SirEnum] which emulates Kotlin packages.
 */
public interface SirEnumGenerator {
    public fun FqName.sirPackageEnum(): SirEnum
    public val collectedPackages: Set<FqName>
}

/**
 * Names SIR declarations that are constructed from [KaDeclarationSymbol].
 */
public interface SirDeclarationNamer {
    public fun KaDeclarationSymbol.sirDeclarationName(): String
}

public sealed interface SirTranslationResult {
    public val allDeclarations: List<SirDeclaration>
    public val primaryDeclaration: SirDeclaration?

    public sealed interface TypeDeclaration : SirTranslationResult {
        public val declaration: SirNamedDeclaration
        override val primaryDeclaration: SirDeclaration? get() = declaration
    }

    public data class Untranslatable(public val origin: SirOrigin) : SirTranslationResult {
        override val allDeclarations: List<SirDeclaration> get() = emptyList()
        override val primaryDeclaration: SirDeclaration? get() = null
    }

    public data class RegularClass(public override val declaration: SirClass) : TypeDeclaration {
        override val allDeclarations: List<SirDeclaration> = listOf(declaration)
    }

    public data class TypeAlias(public override val declaration: SirTypealias) : TypeDeclaration {
        override val allDeclarations: List<SirDeclaration> = listOf(declaration)
    }

    public data class Constructor(public val declaration: SirInit) : SirTranslationResult {
        override val allDeclarations: List<SirDeclaration> = listOf(declaration)
        override val primaryDeclaration: SirDeclaration get() = declaration
    }

    public data class RegularProperty(public val declaration: SirVariable) : SirTranslationResult {
        override val allDeclarations: List<SirDeclaration> = listOf(declaration)
        override val primaryDeclaration: SirDeclaration get() = declaration

    }

    public data class ExtensionProperty(public val getter: SirFunction, public val setter: SirFunction?) : SirTranslationResult {
        override val allDeclarations: List<SirDeclaration> = listOfNotNull(getter, setter)
        override val primaryDeclaration: SirDeclaration get() = getter
    }

    public data class RegularFunction(public val declaration: SirFunction) : SirTranslationResult {
        override val allDeclarations: List<SirDeclaration> = listOf(declaration)
        override val primaryDeclaration: SirDeclaration get() = declaration
    }

    public data class OperatorFunction(
        public val declaration: SirFunction,
        public val supplementaryDeclarations: List<SirDeclaration>
    ) : SirTranslationResult {
        override val allDeclarations: List<SirDeclaration> = listOf(declaration) + supplementaryDeclarations
        override val primaryDeclaration: SirDeclaration get() = declaration
    }

    public data class OperatorSubscript(
        public val declaration: SirSubscript,
        public val supplementaryDeclarations: List<SirDeclaration>
    ) : SirTranslationResult {
        override val allDeclarations: List<SirDeclaration> = listOf(declaration) + supplementaryDeclarations
        override val primaryDeclaration: SirDeclaration get() = declaration
    }

    public data class RegularInterface(
        public val declaration: SirProtocol,
        public val bridgedImplementation: SirExtension?,
        public val markerDeclaration: SirProtocol,
        public val existentialExtension: SirExtension,
        public val samConverter: SirDeclaration?,
    ) : SirTranslationResult {
        override val primaryDeclaration: SirDeclaration get() = declaration
        override val allDeclarations: List<SirDeclaration> =
            listOfNotNull(
                declaration,
                bridgedImplementation,
                markerDeclaration,
                existentialExtension,
                samConverter,
            )
    }

    public data class StubClass(
        public val declaration: SirClass,
    ) : SirTranslationResult {
        override val primaryDeclaration: SirDeclaration get() = declaration
        override val allDeclarations: List<SirDeclaration> = listOfNotNull(declaration)
    }

    public data class StubInterface(
        public val declaration: SirProtocol,
    ) : SirTranslationResult {
        override val primaryDeclaration: SirDeclaration get() = declaration
        override val allDeclarations: List<SirDeclaration> = listOfNotNull(declaration)
    }
}

/**
 * A single entry point to create a lazy wrapper around the given [KaDeclarationSymbol].
 */
public interface SirDeclarationProvider {
    public fun KaDeclarationSymbol.toSir(): SirTranslationResult

    @Deprecated(
        "This is provided for compatibility with external code. Prefer structured result version",
        level = DeprecationLevel.WARNING,
        replaceWith = ReplaceWith("this.toSIR().allDeclarations")
    )
    public fun KaDeclarationSymbol.sirDeclarations(): List<SirDeclaration> = toSir().allDeclarations
}

/**
 * Given [KaDeclarationSymbol] will produce [SirDeclarationParent], representing the parent for corresponding sir node.
 *
 * For example, given the top level function without a package - will return SirModule that should declare that declarations.
 * Or, given the top level function with a package - will return SirExtension for that package.
 *
 */
public interface SirParentProvider {
    public fun KaDeclarationSymbol.getSirParent(ktAnalysisSession: KaSession): SirDeclarationParent

    /**
     * Get original sir parent
     * Some bridged kotlin declaration is unsuitable for hosting other declarations in swift (e.g. protocols, packaged top-levels etc).
     * When that is the case, [SirParentProvider] attempts to relocate children declarations into the most appropriate place.
     * This method returns the original intended parent declaration that the receiver may have been relocated from.
     *
     * @param ktAnalysisSession session
     * @return Sir element for original parent symbol. This is the same as [getSirParent] if the receiver was never relocated.
     */
    public fun KaDeclarationSymbol.getOriginalSirParent(ktAnalysisSession: KaSession): SirElement
}

/**
 *  Provides trampoline declarations for a given [SirDeclaration], if any.
 */
public interface SirTrampolineDeclarationsProvider {
    public fun SirDeclaration.trampolineDeclarations(): List<SirDeclaration>
}

/**
 * Translates the given [KaModule] to the corresponding [SirModule].
 * Note that it is not always a 1-1 mapping.
 */
public interface SirModuleProvider {

    public fun KaModule.sirModule(): SirModule
}

// TODO: SirChildrenProvider probably does not make much sense as a provider,
//  as it acts as a combination of several other provider (declaration, trampoline, visibility)
public interface SirChildrenProvider {

    public fun KaScope.extractDeclarations(ktAnalysisSession: KaSession): Sequence<SirDeclaration> =
        declarations.extractDeclarations(ktAnalysisSession)

    public fun Sequence<KaDeclarationSymbol>.extractDeclarations(kaSession: KaSession): Sequence<SirDeclaration>
}

public interface SirTypeProvider {

    public val errorTypeStrategy: ErrorTypeStrategy
    public val unsupportedTypeStrategy: ErrorTypeStrategy

    public enum class ErrorTypeStrategy {
        Fail, ErrorType
    }

    /**
     * Translates the given [KaType] to [SirType].
     * Calls [reportErrorType] / [reportUnsupportedType] if error/unsupported type
     * is encountered and [errorTypeStrategy] / [unsupportedTypeStrategy] instructs to fail.
     *
     * [processTypeImports] is called with the imports required to use the resulting type properly.
     */
    public fun KaType.translateType(
        ktAnalysisSession: KaSession,
        reportErrorType: (String) -> Nothing,
        reportUnsupportedType: () -> Nothing,
        processTypeImports: (List<SirImport>) -> Unit,
    ): SirType
}

/**
 * Generates a list of [SirBridge] for given [SirDeclaration].
 */
public interface SirBridgeProvider {
    public fun generateFunctionBridge(
        baseBridgeName: String,
        explicitParameters: List<SirParameter>,
        returnType: SirType,
        kotlinFqName: List<String>,
        selfParameter: SirParameter?,
        extensionReceiverParameter: SirParameter?,
        errorParameter: SirParameter?
    ): BridgeFunctionProxy?

    public fun generateTypeBridge(
        kotlinFqName: List<String>,
        swiftFqName: String,
        swiftSymbolName: String,
    ): SirTypeBindingBridge?
}

/**
 * Matches a [SirType] to its declaration name in either kotlin or swift.
 */
public interface SirTypeNamer {
    public enum class KotlinNameType {
        FQN, PARAMETRIZED
    }

    public fun swiftFqName(type: SirType): String
    public fun kotlinFqName(sirType: SirType, nameType: KotlinNameType): String
}

public fun SirTypeNamer(): SirTypeNamer = StandaloneSirTypeNamer

public interface SirVisibilityChecker {
    /**
     * Determines visibility of the given [KaDeclarationSymbol].
     * @return null if symbol should not be exposed to SIR completely.
     */
    @Deprecated(
        "Use sirAvailability instead",
        level = DeprecationLevel.WARNING,
        replaceWith = ReplaceWith("this.sirAvailability(ktAnalysisSession)")
    )
    public fun KaDeclarationSymbol.sirVisibility(ktAnalysisSession: KaSession): SirVisibility? =
        this.sirAvailability(ktAnalysisSession).visibility

    /**
     * Determines availability of the given [KaDeclarationSymbol].
     */
    public fun KaDeclarationSymbol.sirAvailability(ktAnalysisSession: KaSession): SirAvailability
}

/**
 * TODO: KT-78603 drop this [KaSession] inheritance as it is prohibited and can lead to unexpected problems
 */
@OptIn(KaImplementationDetail::class)
public interface SirAndKaSession : KaSession, SirSession

public inline fun <T> SirSession.withSessions(crossinline block: SirAndKaSession.() -> T): T {
    return analyze(this.useSiteModule) {
        @OptIn(KaImplementationDetail::class)
        object : SirSession by this@withSessions, KaSession by this@analyze, SirAndKaSession {
            override val useSiteModule: KaModule get() = this@analyze.useSiteModule
        }.block()
    }
}