/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.react.tasks

import com.facebook.react.model.ModelAutolinkingConfigJson
import com.facebook.react.model.ModelAutolinkingDependenciesJson
import com.facebook.react.model.ModelAutolinkingDependenciesPlatformAndroidJson
import com.facebook.react.model.ModelAutolinkingDependenciesPlatformJson
import com.facebook.react.tasks.GenerateAutolinkingNewArchitecturesFileTask.Companion.sanitizeCmakeListsPath
import com.facebook.react.tests.createTestTask
import org.assertj.core.api.Assertions.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder

class GenerateAutolinkingNewArchitecturesFileTaskTest {

  @get:Rule val tempFolder = TemporaryFolder()

  @Test
  fun generatePackageListTask_groupIsSetCorrectly() {
    val task = createTestTask<GenerateAutolinkingNewArchitecturesFileTask> {}
    assertThat(task.group).isEqualTo("react")
  }

  @Test
  fun generatePackageListTask_staticInputs_areSetCorrectly() {
    val outputFolder = tempFolder.newFolder("build")
    val inputFile = tempFolder.newFile("config.json")

    val task =
        createTestTask<GenerateAutolinkingNewArchitecturesFileTask> {
          it.generatedOutputDirectory.set(outputFolder)
          it.autolinkInputFile.set(inputFile)
        }

    assertThat(task.generatedOutputDirectory.get().asFile).isEqualTo(outputFolder)
    assertThat(task.generatedOutputDirectory.get().asFile).isEqualTo(outputFolder)
  }

  @Test
  fun filterAndroidPackages_withNull_returnsEmpty() {
    val task = createTestTask<GenerateAutolinkingNewArchitecturesFileTask>()
    val result = task.filterAndroidPackages(null)
    assertThat(result).isEmpty()
  }

  @Test
  fun filterAndroidPackages_withEmptyObject_returnsEmpty() {
    val task = createTestTask<GenerateAutolinkingNewArchitecturesFileTask>()
    val result = task.filterAndroidPackages(ModelAutolinkingConfigJson("1000.0.0", null, null))
    assertThat(result).isEmpty()
  }

  @Test
  fun filterAndroidPackages_withNoAndroidObject_returnsEmpty() {
    val task = createTestTask<GenerateAutolinkingNewArchitecturesFileTask>()
    val result =
        task.filterAndroidPackages(
            ModelAutolinkingConfigJson(
                reactNativeVersion = "1000.0.0",
                dependencies =
                    mapOf(
                        "a-dependency" to
                            ModelAutolinkingDependenciesJson(
                                root = "./a/directory",
                                name = "a-dependency",
                                platforms =
                                    ModelAutolinkingDependenciesPlatformJson(android = null))),
                project = null))
    assertThat(result).isEmpty()
  }

  @Test
  fun filterAndroidPackages_withValidAndroidObject_returnsIt() {
    val task = createTestTask<GenerateAutolinkingNewArchitecturesFileTask>()
    val android =
        ModelAutolinkingDependenciesPlatformAndroidJson(
            sourceDir = "./a/directory/android",
            packageImportPath = "import com.facebook.react.aPackage;",
            packageInstance = "new APackage()",
            buildTypes = emptyList(),
        )

    val result =
        task.filterAndroidPackages(
            ModelAutolinkingConfigJson(
                reactNativeVersion = "1000.0.0",
                dependencies =
                    mapOf(
                        "a-dependency" to
                            ModelAutolinkingDependenciesJson(
                                root = "./a/directory",
                                name = "a-dependency",
                                platforms =
                                    ModelAutolinkingDependenciesPlatformJson(android = android))),
                project = null))
    assertThat(result).containsExactly(android)
  }

  @Test
  fun generateCmakeFileContent_withNoPackages_returnsEmpty() {
    val output =
        createTestTask<GenerateAutolinkingNewArchitecturesFileTask>()
            .generateCmakeFileContent(emptyList())
    // language=cmake
    assertThat(output)
        .isEqualTo(
            """
      # This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin)
      cmake_minimum_required(VERSION 3.13)
      set(CMAKE_VERBOSE_MAKEFILE on)
      
      # We set REACTNATIVE_MERGED_SO so libraries/apps can selectively decide to depend on either libreactnative.so
      # or link against a old prefab target (this is needed for React Native 0.76 on).
      set(REACTNATIVE_MERGED_SO true)
      
      
      
      set(AUTOLINKED_LIBRARIES
        
      )
    """
                .trimIndent())
  }

  @Test
  fun generateCmakeFileContent_withPackages_returnsImportCorrectly() {
    val output =
        createTestTask<GenerateAutolinkingNewArchitecturesFileTask>()
            .generateCmakeFileContent(testDependencies)
    // language=cmake
    assertThat(output)
        .isEqualTo(
            """
      # This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin)
      cmake_minimum_required(VERSION 3.13)
      set(CMAKE_VERBOSE_MAKEFILE on)
      
      # We set REACTNATIVE_MERGED_SO so libraries/apps can selectively decide to depend on either libreactnative.so
      # or link against a old prefab target (this is needed for React Native 0.76 on).
      set(REACTNATIVE_MERGED_SO true)
      
      add_subdirectory("./a/directory/" aPackage_autolinked_build)
      add_subdirectory("./another/directory/with\ spaces/" anotherPackage_autolinked_build)
      add_subdirectory("./another/directory/cxx/" anotherPackage_cxxmodule_autolinked_build)
      
      set(AUTOLINKED_LIBRARIES
        react_codegen_aPackage
        react_codegen_anotherPackage
      another_cxxModule
      )
    """
                .trimIndent())
  }

  @Test
  fun generateCppFileContent_withNoPackages_returnsEmpty() {
    val output =
        createTestTask<GenerateAutolinkingNewArchitecturesFileTask>()
            .generateCppFileContent(emptyList())
    // language=cpp
    assertThat(output)
        .isEqualTo(
            """
      /**
       * This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin).
       *
       * Do not edit this file as changes may cause incorrect behavior and will be lost
       * once the code is regenerated.
       *
       */

      #include "autolinking.h"


      namespace facebook {
      namespace react {

      std::shared_ptr<TurboModule> autolinking_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams &params) {

        return nullptr;
      }

      std::shared_ptr<TurboModule> autolinking_cxxModuleProvider(const std::string moduleName, const std::shared_ptr<CallInvoker>& jsInvoker) {

        return nullptr;
      }

      void autolinking_registerProviders(std::shared_ptr<ComponentDescriptorProviderRegistry const> providerRegistry) {

        return;
      }

      } // namespace react
      } // namespace facebook
    """
                .trimIndent())
  }

  @Test
  fun generateCppFileContent_withPackages_returnsImportCorrectly() {
    val output =
        createTestTask<GenerateAutolinkingNewArchitecturesFileTask>()
            .generateCppFileContent(testDependencies)
    // language=cpp
    assertThat(output)
        .isEqualTo(
            """
      /**
       * This code was generated by [React Native](https://www.npmjs.com/package/@react-native/gradle-plugin).
       *
       * Do not edit this file as changes may cause incorrect behavior and will be lost
       * once the code is regenerated.
       *
       */

      #include "autolinking.h"
      #include <aPackage.h>
      #include <anotherPackage.h>
      #include <react/renderer/components/anotherPackage/ComponentDescriptors.h>
      #include <AnotherCxxModule.h>

      namespace facebook {
      namespace react {

      std::shared_ptr<TurboModule> autolinking_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams &params) {
      auto module_aPackage = aPackage_ModuleProvider(moduleName, params);
      if (module_aPackage != nullptr) {
      return module_aPackage;
      }
      auto module_anotherPackage = anotherPackage_ModuleProvider(moduleName, params);
      if (module_anotherPackage != nullptr) {
      return module_anotherPackage;
      }
        return nullptr;
      }

      std::shared_ptr<TurboModule> autolinking_cxxModuleProvider(const std::string moduleName, const std::shared_ptr<CallInvoker>& jsInvoker) {
      if (moduleName == AnotherCxxModule::kModuleName) {
      return std::make_shared<AnotherCxxModule>(jsInvoker);
      }
        return nullptr;
      }

      void autolinking_registerProviders(std::shared_ptr<ComponentDescriptorProviderRegistry const> providerRegistry) {
      providerRegistry->add(concreteComponentDescriptorProvider<AnotherPackageComponentDescriptor>());
        return;
      }

      } // namespace react
      } // namespace facebook
    """
                .trimIndent())
  }

  @Test
  fun sanitizeCmakeListsPath_withPathEndingWithFileName_removesFilename() {
    val input = "./a/directory/CMakeLists.txt"
    assertThat(sanitizeCmakeListsPath(input)).isEqualTo("./a/directory/")
  }

  @Test
  fun sanitizeCmakeListsPath_withSpaces_removesSpaces() {
    val input = "./a/dir ectory/with spaces/"
    assertThat(sanitizeCmakeListsPath(input)).isEqualTo("./a/dir\\ ectory/with\\ spaces/")
  }

  @Test
  fun sanitizeCmakeListsPath_withPathEndingWithFileNameAndSpaces_sanitizesIt() {
    val input = "./a/dir ectory/CMakeLists.txt"
    assertThat(sanitizeCmakeListsPath(input)).isEqualTo("./a/dir\\ ectory/")
  }

  private val testDependencies =
      listOf(
          ModelAutolinkingDependenciesPlatformAndroidJson(
              sourceDir = "./a/directory",
              packageImportPath = "import com.facebook.react.aPackage;",
              packageInstance = "new APackage()",
              buildTypes = emptyList(),
              libraryName = "aPackage",
              componentDescriptors = emptyList(),
              cmakeListsPath = "./a/directory/CMakeLists.txt",
          ),
          ModelAutolinkingDependenciesPlatformAndroidJson(
              sourceDir = "./another/directory",
              packageImportPath = "import com.facebook.react.anotherPackage;",
              packageInstance = "new AnotherPackage()",
              buildTypes = emptyList(),
              libraryName = "anotherPackage",
              componentDescriptors = listOf("AnotherPackageComponentDescriptor"),
              cmakeListsPath = "./another/directory/with spaces/CMakeLists.txt",
              cxxModuleCMakeListsPath = "./another/directory/cxx/CMakeLists.txt",
              cxxModuleHeaderName = "AnotherCxxModule",
              cxxModuleCMakeListsModuleName = "another_cxxModule",
          ))
}
