wip
diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/variantPublishing.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/variantPublishing.kt
index 5c2f3b3d..a9a2051 100644
--- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/variantPublishing.kt
+++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/pm20/variantPublishing.kt
@@ -8,6 +8,7 @@
 import com.android.build.gradle.internal.publishing.AndroidArtifacts
 import org.gradle.api.Project
 import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.Dependency
 import org.gradle.api.artifacts.ModuleDependency
 import org.gradle.api.artifacts.PublishArtifact
 import org.gradle.api.attributes.*
@@ -19,15 +20,23 @@
 import org.gradle.api.publish.maven.MavenPublication
 import org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication
 import org.gradle.api.tasks.bundling.AbstractArchiveTask
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
+import org.jetbrains.kotlin.gradle.dsl.pm20Extension
+import org.jetbrains.kotlin.gradle.dsl.topLevelExtensionOrNull
 import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
+import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.*
 import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.ComputedCapability
 import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.copyAttributes
 import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.copyConfigurationForPublishing
 import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.disambiguateName
 import org.jetbrains.kotlin.gradle.plugin.mpp.publishedConfigurationName
+import org.jetbrains.kotlin.gradle.plugin.mpp.toModuleDependency
 import org.jetbrains.kotlin.gradle.plugin.usageByName
 import org.jetbrains.kotlin.gradle.tasks.withType
 import org.jetbrains.kotlin.gradle.utils.dashSeparatedName
+import org.jetbrains.kotlin.project.model.KotlinModuleDependency
+import org.jetbrains.kotlin.project.model.LocalModuleIdentifier
+import org.jetbrains.kotlin.project.model.MavenModuleIdentifier
 import java.util.*
 import javax.inject.Inject
 
@@ -103,7 +112,11 @@
             project.objects.newInstance(VariantPublishingConfigurator::class.java, project)
     }
 
-    open fun platformComponentName(variant: KotlinGradleVariant) = variant.disambiguateName("")
+    open fun platformComponentName(variant: KotlinGradleVariant) = when (variant.containingModule.publicationMode) {
+        Private -> error("software component is prohibited for non-published $variant")
+        is Standalone -> variant.disambiguateName("")
+        is Embedded -> variant.name // Use the same software component for same-named variants in all modules
+    }
 
     open fun inferMavenScope(variant: KotlinGradleVariant, configurationName: String): String? =
         when {
@@ -115,40 +128,57 @@
     open fun configurePublishing(
         request: PlatformPublicationToMavenRequest
     ) {
-        val componentName = request.componentName
+        request.fromModule.ifMadePublic {
+            val componentName = request.componentName
 
-        registerPlatformModulePublication(
-            componentName,
-            request.publicationHolder,
-            request.variantPublicationRequests,
-            request.fromModule::ifMadePublic
-        )
+            registerPlatformModulePublication(
+                componentName,
+                request.publicationHolder,
+                request.variantPublicationRequests
+            )
 
-        val publishFromVariants = request.variantPublicationRequests.mapTo(mutableSetOf()) { it.fromVariant }
+            val publishFromVariants = request.variantPublicationRequests.mapTo(mutableSetOf()) { it.fromVariant }
 
-        // Collecting sources for multiple variants is not yet supported;
-        // TODO make callers provide the source variants?
-        // The MPP plugin doesn't publish the source artifacts as variants; keep that behavior for legacy-mapped variants for now
-        if (
-            publishFromVariants.size == 1 &&
-            publishFromVariants.none { it is LegacyMappedVariant }
-        ) {
-            val singlePublishedVariant = publishFromVariants.single()
-            configureSourceElementsPublishing(componentName, singlePublishedVariant)
+            // Collecting sources for multiple variants is not yet supported;
+            // TODO make callers provide the source variants?
+            // The MPP plugin doesn't publish the source artifacts as variants; keep that behavior for legacy-mapped variants for now
+            if (
+                publishFromVariants.size == 1 &&
+                publishFromVariants.none { it is LegacyMappedVariant }
+            ) {
+                val singlePublishedVariant = publishFromVariants.single()
+                configureSourceElementsPublishing(componentName, singlePublishedVariant, request.publicationHolder)
+            }
+
+            registerPlatformVariantsInRootModule(request)
         }
-
-        registerPlatformVariantsInRootModule(
-            request.publicationHolder,
-            request.fromModule,
-            request.variantPublicationRequests
-        )
     }
 
-    protected open fun configureSourceElementsPublishing(componentName: String, variant: KotlinGradleVariant) {
+    protected open fun configureSourceElementsPublishing(
+        componentName: String,
+        variant: KotlinGradleVariant,
+        publishedModuleHolder: SingleMavenPublishedModuleHolder
+    ) {
         val configurationName = variant.disambiguateName("sourceElements")
+
+        // FIXME create this one not only in ifMadePublic but before that unconditionally?
         val docsVariants = DocumentationVariantConfigurator().createSourcesElementsConfiguration(configurationName, variant)
+
+        val docsVariantForPublishing = copyConfigurationForPublishing(
+            project,
+            docsVariants.name + "-published",
+            docsVariants,
+            overrideCapabilities = {
+                val capability =
+                    ComputedCapability.forPublishedPlatformVariant(variant, publishedModuleHolder)
+                if (capability != null) {
+                    outgoing.capability(capability)
+                }
+            }
+        )
+
         project.components.withType(AdhocComponentWithVariants::class.java).named(componentName).configure { component ->
-            component.addVariantsFromConfiguration(docsVariants) { }
+            component.addVariantsFromConfiguration(docsVariantForPublishing) { }
         }
     }
 
@@ -158,77 +188,110 @@
      * Assigns the created Maven publication to the [publishedModuleHolder].
      */
     protected open fun registerPlatformModulePublication(
+        module: KotlinGradleModule,
         componentName: String,
         publishedModuleHolder: SingleMavenPublishedModuleHolder,
-        variantRequests: Iterable<VariantPublicationRequest>,
-        whenShouldRegisterPublication: (() -> Unit) -> Unit
+        variantRequests: Iterable<VariantPublicationRequest>
     ) {
-        val platformComponent = softwareComponentFactory.adhoc(componentName)
-        project.components.add(platformComponent)
+        module.ifMadePublic {
+            val platformComponent =
+                project.components.withType(AdhocComponentWithVariants::class.java).findByName(componentName)
+                    ?: softwareComponentFactory.adhoc(componentName).also { project.components.add(it) }
 
-        variantRequests.forEach { request ->
-            val originalConfiguration = request.publishConfiguration
-            val mavenScopeOrNull = inferMavenScope(request.fromVariant, originalConfiguration.name)
+            variantRequests.forEach { request ->
+                val originalConfiguration = request.publishConfiguration
+                val mavenScopeOrNull = inferMavenScope(request.fromVariant, originalConfiguration.name)
 
-            val publishedConfiguration = copyConfigurationForPublishing(
-                request.fromVariant.project,
-                newName = publishedConfigurationName(originalConfiguration.name) + "-platform",
-                configuration = originalConfiguration,
-                overrideArtifacts = (request as? AdvancedVariantPublicationRequest)
-                    ?.overrideConfigurationArtifactsForPublication
-                    ?.let { override -> { artifacts -> artifacts.addAllLater(override) } },
-                overrideAttributes = (request as? AdvancedVariantPublicationRequest)
-                    ?.overrideConfigurationAttributesForPublication
-                    ?.let { override -> { attributes -> copyAttributes(override, attributes) } }
-            )
-
-            platformComponent.addVariantsFromConfiguration(publishedConfiguration) details@{ variantDetails ->
-                mavenScopeOrNull?.let { variantDetails.mapToMavenScope(it) }
-            }
-        }
-
-        whenShouldRegisterPublication {
-            project.pluginManager.withPlugin("maven-publish") {
-                project.extensions.getByType(PublishingExtension::class.java).apply {
-                    publications.create(componentName, MavenPublication::class.java).apply {
-                        (this as DefaultMavenPublication).isAlias = true
-                        from(platformComponent)
-                        publishedModuleHolder.assignMavenPublication(this)
-                        artifactId = dashSeparatedName(
-                            project.name, publishedModuleHolder.defaultPublishedModuleSuffix
-                        ).toLowerCase(Locale.ENGLISH)
+                val publishedConfiguration = copyConfigurationForPublishing(
+                    request.fromVariant.project,
+                    newName = publishedConfigurationName(originalConfiguration.name) + "-platform",
+                    configuration = originalConfiguration,
+                    overrideArtifacts = (request as? AdvancedVariantPublicationRequest)
+                        ?.overrideConfigurationArtifactsForPublication
+                        ?.let { override -> { artifacts -> artifacts.addAllLater(override) } },
+                    overrideAttributes = (request as? AdvancedVariantPublicationRequest)
+                        ?.overrideConfigurationAttributesForPublication
+                        ?.let { override -> { attributes -> copyAttributes(override, attributes) } },
+                    overrideDependencies = {
+                        addAllLater(project.listProperty {
+                            replaceProjectDependenciesWithPublishedMavenDependencies(
+                                project,
+                                originalConfiguration.allDependencies
+                            )
+                        })
+                    },
+                    overrideCapabilities = {
+                        ComputedCapability.forPublishedPlatformVariant(variant, publishedModuleHolder)
+                            ?.let(outgoing::capability)
                     }
+                )
+
+                platformComponent.addVariantsFromConfiguration(publishedConfiguration) details@{ variantDetails ->
+                    mavenScopeOrNull?.let { variantDetails.mapToMavenScope(it) }
+                }
+            }
+
+            project.pluginManager.withPlugin("maven-publish") {
+                val publication = project.extensions.getByType(PublishingExtension::class.java).run {
+                    if (module.publicationMode is Standalone) {
+                        publications.create(componentName, MavenPublication::class.java).apply {
+                            // TODO: remove internal API usage. This prevents Gradle from reporting errors during publication because of multiple
+                            //       Maven publications with different coordinates
+                            (this as DefaultMavenPublication).isAlias = true
+
+                            from(platformComponent)
+                            artifactId = dashSeparatedName(
+                                project.name, publishedModuleHolder.defaultPublishedModuleSuffix
+                            ).toLowerCase(Locale.ENGLISH)
+                        }
+                    } else {
+                        // TODO still create the publication for embedded module's variant if one with this name is absent in the main module?
+                        publications.findByName(componentName) as? MavenPublication
+                    }
+                }
+
+                if (publication != null) {
+                    publishedModuleHolder.assignMavenPublication(publication)
                 }
             }
         }
     }
 
     protected open fun registerPlatformVariantsInRootModule(
-        publishedModuleHolder: SingleMavenPublishedModuleHolder,
-        kotlinModule: KotlinGradleModule,
-        variantRequests: Iterable<VariantPublicationRequest>
+        request: PlatformPublicationToMavenRequest
     ) {
         val platformModuleDependencyProvider = project.provider {
-            val coordinates = publishedModuleHolder.publishedMavenModuleCoordinates
+            val variants = request.variantPublicationRequests.mapTo(mutableSetOf()) { it.fromVariant }
+            val singleVariant = variants.singleOrNull() ?: error("expected single variant: ${variants.joinToString()}") // TODO NOW: test and remove?
+            val coordinates = request.publicationHolder.publishedMavenModuleCoordinates
             (project.dependencies.create("${coordinates.group}:${coordinates.name}:${coordinates.version}") as ModuleDependency).apply {
-                if (kotlinModule.moduleClassifier != null) {
-                    capabilities { it.requireCapability(ComputedCapability.fromModule(kotlinModule)) }
+                capabilities {
+                    val capability = ComputedCapability.forPublishedPlatformVariant(singleVariant, request.publicationHolder)
+                    if (capability != null) {
+                        it.requireCapability(capability)
+                    }
                 }
             }
         }
 
-        val rootSoftwareComponent =
-            project.components
+        val rootSoftwareComponent = when (request.fromModule.publicationMode) {
+            Private -> error("expected to be published")
+            Embedded -> project.components
                 .withType(AdhocComponentWithVariants::class.java)
-                .getByName(rootPublicationComponentName(kotlinModule))
+                .getByName(rootPublicationComponentName(project.pm20Extension.main))
+            is Standalone ->
+                project.components
+                    .withType(AdhocComponentWithVariants::class.java)
+                    .getByName(rootPublicationComponentName(request.fromModule))
+        }
 
-        variantRequests.forEach { variantRequest ->
+        request.variantPublicationRequests.forEach { variantRequest ->
             val configuration = variantRequest.publishConfiguration
             project.configurations.create(publishedConfigurationName(configuration.name)).apply {
                 isCanBeConsumed = false
                 isCanBeResolved = false
 
-                setModuleCapability(this, kotlinModule)
+                setGradlePublishedModuleCapability(this, request.fromModule)
                 dependencies.addLater(platformModuleDependencyProvider)
                 copyAttributes(configuration.attributes, this.attributes)
                 rootSoftwareComponent.addVariantsFromConfiguration(this) { }
@@ -280,7 +343,116 @@
             configurationName,
             sourcesArtifactTask.get(),
             artifactClassifier,
-            ComputedCapability.fromModuleOrNull(variant.containingModule)
+            ComputedCapability.forProjectDependenciesOnModule(variant.containingModule)
         )
     }
-}
\ No newline at end of file
+}
+
+
+internal data class VersionedMavenModuleIdentifier(val moduleId: MavenModuleIdentifier, val version: String)
+
+internal fun localModuleDependenciesToPublishedModuleMapping(
+    project: Project,
+    dependencies: Iterable<KotlinModuleDependency>
+): Map<KotlinModuleDependency, VersionedMavenModuleIdentifier> {
+    return dependencies.mapNotNull mapping@{ dependency ->
+        val moduleIdentifier = dependency.moduleIdentifier
+        val resolvesToProject =
+            if (moduleIdentifier is LocalModuleIdentifier && moduleIdentifier.buildId == project.currentBuildId().name)
+                project.project(moduleIdentifier.projectId)
+            else
+                return@mapping null
+
+        val moduleClassifier = moduleIdentifier.moduleClassifier
+        when (val ext = resolvesToProject.topLevelExtensionOrNull) {
+            is KotlinPm20ProjectExtension -> {
+                val module = ext.modules.find { it.moduleClassifier == moduleClassifier } ?: return@mapping null
+
+                when (module.publicationMode) {
+                    Private -> {
+                        error("A dependency on $module can't be published because the module is not published.")
+                    }
+
+                    is Standalone, Embedded -> {
+                        val coordinates = module.publicationHolder()?.publishedMavenModuleCoordinates
+                            ?: return@mapping null
+
+                        dependency to VersionedMavenModuleIdentifier(
+                            MavenModuleIdentifier(
+                                coordinates.group,
+                                coordinates.name,
+                                module.moduleClassifier.takeIf { module.publicationMode is Embedded }
+                            ),
+                            coordinates.version
+                        )
+                    }
+                }
+            }
+
+            is KotlinMultiplatformExtension -> {
+                val rootPublication = ext.rootSoftwareComponent.publicationDelegate
+                val group = rootPublication?.groupId ?: project.group.toString()
+                val name = rootPublication?.artifactId ?: project.name
+                val version = rootPublication?.version ?: project.version.toString()
+                dependency to VersionedMavenModuleIdentifier(MavenModuleIdentifier(group, name, null), version)
+            }
+
+            else -> null
+        }
+    }.toMap()
+}
+
+internal fun replaceProjectDependenciesWithPublishedMavenIdentifiers(
+    project: Project,
+    dependencies: Iterable<KotlinModuleDependency>
+): Set<MavenModuleIdentifier> {
+    val mapping = localModuleDependenciesToPublishedModuleMapping(project, dependencies)
+    return dependencies.mapNotNull { dependency ->
+        val replacement = mapping[dependency]
+        val id = dependency.moduleIdentifier
+        when {
+            replacement != null -> replacement.moduleId
+
+            id is MavenModuleIdentifier -> id
+
+            id is LocalModuleIdentifier && id.buildId == project.currentBuildId().name -> {
+                val otherProject = project.project(id.projectId)
+                // TODO: find single publication with maven-publish in non-MPP projects?
+                MavenModuleIdentifier(otherProject.group.toString(), otherProject.name, otherProject.version.toString())
+            }
+
+            else -> null
+        }
+    }.toSet()
+}
+
+internal fun replaceProjectDependenciesWithPublishedMavenDependencies(
+    project: Project,
+    dependencies: Iterable<Dependency>
+): List<Dependency> {
+    val dependencyToKotlinModuleDependency = dependencies.associateWith { it.toModuleDependency(project) }
+    val mapping = localModuleDependenciesToPublishedModuleMapping(project, dependencyToKotlinModuleDependency.values)
+    return dependencies.map { dependency ->
+        val replacement = mapping[dependencyToKotlinModuleDependency.getValue(dependency)]
+        if (replacement != null)
+            project.dependencies.create("${replacement.moduleId.group}:${replacement.moduleId.name}:${replacement.version}").apply {
+                if (replacement.moduleId.moduleClassifier != null) {
+                    (this as ModuleDependency).capabilities {
+                        it.requireCapability(checkNotNull(ComputedCapability.forAuxiliaryModuleByCoordinatesAndName(project, replacement)))
+                    }
+                }
+            }
+        else {
+            dependency
+        }
+    }
+
+}
+
+internal fun KotlinGradleModule.publicationHolder(): SingleMavenPublishedModuleHolder? =
+    when (this) {
+        is KotlinGradleModuleInternal -> publicationHolder
+        // TODO NOW: Wtf?
+        is SingleMavenPublishedModuleHolder -> this
+        else -> null
+    }