/*
 * Copyright (c) Facebook, Inc. and its 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.turbomodule.core;

import androidx.annotation.Nullable;
import com.facebook.infer.annotation.Assertions;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.react.ReactPackage;
import com.facebook.react.TurboReactPackage;
import com.facebook.react.bridge.CxxModuleWrapper;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class ReactPackageTurboModuleManagerDelegate extends TurboModuleManagerDelegate {
  private final List<TurboReactPackage> mPackages = new ArrayList<>();
  private final Map<String, TurboModule> mModules = new HashMap<>();
  private final ReactApplicationContext mReactApplicationContext;

  protected ReactPackageTurboModuleManagerDelegate(
      ReactApplicationContext reactApplicationContext, List<ReactPackage> packages) {
    super();
    mReactApplicationContext = reactApplicationContext;
    for (ReactPackage reactPackage : packages) {
      if (reactPackage instanceof TurboReactPackage) {
        mPackages.add((TurboReactPackage) reactPackage);
      }
    }
  }

  @Nullable
  @Override
  public TurboModule getModule(String moduleName) {
    TurboModule module = resolveModule(moduleName);
    if (module == null) {
      return null;
    }

    if (module instanceof CxxModuleWrapper) {
      return null;
    }

    return module;
  }

  @Nullable
  @Override
  @DoNotStrip
  public CxxModuleWrapper getLegacyCxxModule(String moduleName) {
    TurboModule module = resolveModule(moduleName);
    if (module == null) {
      return null;
    }

    if (!(module instanceof CxxModuleWrapper)) {
      return null;
    }

    return (CxxModuleWrapper) module;
  }

  private TurboModule resolveModule(String moduleName) {
    if (mModules.containsKey(moduleName)) {
      return mModules.get(moduleName);
    }

    NativeModule resolvedModule = null;

    for (final TurboReactPackage pkg : mPackages) {
      try {
        NativeModule module = pkg.getModule(moduleName, mReactApplicationContext);
        if (resolvedModule == null || module != null && module.canOverrideExistingModule()) {
          resolvedModule = module;
        }
      } catch (IllegalArgumentException ex) {
        /**
         * TurboReactPackages can throw an IllegalArgumentException when a module isn't found. If
         * this happens, it's safe to ignore the exception because a later TurboReactPackage could
         * provide the module.
         */
      }
    }

    if (resolvedModule instanceof TurboModule) {
      mModules.put(moduleName, (TurboModule) resolvedModule);
    } else {
      /**
       * 1. The list of TurboReactPackages doesn't change. 2. TurboReactPackage.getModule is
       * deterministic. Therefore, any two invocations of TurboReactPackage.getModule will return
       * the same result given that they're provided the same arguments.
       *
       * <p>Hence, if module lookup fails once, we know it'll fail every time. Therefore, we can
       * write null to the mModules Map and avoid doing this extra work.
       */
      mModules.put(moduleName, null);
    }

    return mModules.get(moduleName);
  }

  @Override
  public List<String> getEagerInitModuleNames() {
    List<String> moduleNames = new ArrayList<>();
    for (TurboReactPackage reactPackage : mPackages) {
      for (ReactModuleInfo moduleInfo :
          reactPackage.getReactModuleInfoProvider().getReactModuleInfos().values()) {

        if (moduleInfo.isTurboModule() && moduleInfo.needsEagerInit()) {
          moduleNames.add(moduleInfo.name());
        }
      }
    }
    return moduleNames;
  }

  public abstract static class Builder {
    private @Nullable List<ReactPackage> mPackages;
    private @Nullable ReactApplicationContext mContext;

    public Builder setPackages(List<ReactPackage> packages) {
      mPackages = new ArrayList<>(packages);
      return this;
    }

    public Builder setReactApplicationContext(ReactApplicationContext context) {
      mContext = context;
      return this;
    }

    protected abstract ReactPackageTurboModuleManagerDelegate build(
        ReactApplicationContext context, List<ReactPackage> packages);

    public ReactPackageTurboModuleManagerDelegate build() {
      Assertions.assertNotNull(
          mContext,
          "The ReactApplicationContext must be provided to create ReactPackageTurboModuleManagerDelegate");
      Assertions.assertNotNull(
          mPackages,
          "A set of ReactPackages must be provided to create ReactPackageTurboModuleManagerDelegate");
      return build(mContext, mPackages);
    }
  }
}

@@ -780,60 +780,12 @@
 til.
-HashMap;%0Aimport java.util.List;%0Aimport java.util.Map
+List
 ;%0A%0Ap
@@ -956,77 +956,8 @@
 ();%0A
-  private final Map%3CString, TurboModule%3E mModules = new HashMap%3C%3E();%0A
   pr
@@ -2008,16 +2008,28 @@
 e;%0A  %7D%0A%0A
+  @Nullable%0A
   privat
@@ -2081,98 +2081,8 @@
 ) %7B%0A
-    if (mModules.containsKey(moduleName)) %7B%0A      return mModules.get(moduleName);%0A    %7D%0A%0A
     
@@ -2776,32 +2776,14 @@
     
-mModules.put(moduleName,
+return
  (Tu
@@ -2811,567 +2811,32 @@
 dule
-)
 ;%0A    %7D
- else %7B%0A      /**%0A       * 1. The list of TurboReactPackages doesn't change. 2. TurboReactPackage.getModule is%0A       * deterministic. Therefore, any two invocations of TurboReactPackage.getModule will return%0A       * the same result given that they're provided the same arguments.%0A       *%0A       * %3Cp%3EHence, if module lookup fails once, we know it'll fail every time. Therefore, we can%0A       * write null to the mModules Map and avoid doing this extra work.%0A       */%0A      mModules.put(moduleName, null);%0A    %7D%0A%0A    return mModules.get(moduleName)
+%0A%0A    return null
 ;%0A  

Remove module cache from ReactPackageTurboModuleManagerDelegate