/*
 * 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.uimanager.common

import android.view.View

/**
 * Utility object providing helper methods for working with React Native views.
 *
 * This object contains utilities for determining which UIManager (Legacy/Paper or Fabric) a view
 * belongs to, based on view tags and surface IDs. These utilities are essential for routing events
 * and operations to the correct UIManager implementation.
 *
 * @see UIManagerType
 */
public object ViewUtil {

  /**
   * Constant representing the absence of a surface ID.
   *
   * This value (-1) is used as a placeholder when no surface ID is available, typically indicating
   * that the view or event originated from the legacy (Paper) UIManager rather than Fabric.
   */
  public const val NO_SURFACE_ID: Int = -1

  /**
   * Counter for uniquely identifying views. - % 2 === 0 means it is a Fabric tag. See
   * https://github.com/facebook/react/pull/12587
   *
   * @param viewTag tag of the view this is event is dispatched to
   */
  @JvmStatic
  @UIManagerType
  public fun getUIManagerType(viewTag: Int): Int =
      if (viewTag % 2 == 0) {
        UIManagerType.FABRIC
      } else {
        UIManagerType.LEGACY
      }

  /**
   * Overload for [getUIManagerType] that uses the view's id to determine if it originated from
   * Fabric
   */
  @JvmStatic @UIManagerType public fun getUIManagerType(view: View): Int = getUIManagerType(view.id)

  /**
   * Version of getUIManagerType that uses both surfaceId and viewTag heuristics
   *
   * @param viewTag tag of the view this is event is dispatched to
   * @param surfaceId ID of the corresponding surface
   */
  @Suppress("DEPRECATION")
  @JvmStatic
  @UIManagerType
  public fun getUIManagerType(viewTag: Int, surfaceId: Int): Int {
    // We have a contract that Fabric events *always* have a SurfaceId passed in, and non-Fabric
    // events NEVER have a SurfaceId passed in (the default/placeholder of -1 is passed in instead).
    //
    // Why does this matter?
    // Events can be sent to Views that are part of the View hierarchy *but not directly managed
    // by React Native*. For example, embedded custom hierarchies, Litho hierarchies, etc.
    // In those cases it's important to know that the Event should be sent to the Fabric or
    // non-Fabric UIManager, and we cannot use the ViewTag for inference since it's not controlled
    // by RN and is essentially a random number.
    // At some point it would be great to pass the SurfaceContext here instead.
    @UIManagerType
    val uiManagerType = if (surfaceId == -1) UIManagerType.LEGACY else UIManagerType.FABRIC
    if (uiManagerType == UIManagerType.LEGACY && !isRootTag(viewTag)) {
      // TODO (T123064648): Some events for Fabric still didn't have the surfaceId set, so if it's
      // not a React RootView, double check if the tag belongs to Fabric.
      if (viewTag % 2 == 0) {
        return UIManagerType.FABRIC
      }
    }
    return uiManagerType
  }

  /**
   * @param viewTag react tag
   * @return if the react tag received by parameter is a RootTag or not.
   */
  @Deprecated(
      "You should not check the tag of the view to inspect if it's the rootTag. " +
          "Relying on this logic could make your app/library break in the future.",
      ReplaceWith(""),
  )
  @JvmStatic
  public fun isRootTag(viewTag: Int): Boolean = viewTag % 10 == 1
}
