package com.volcengine.reactnative.veplayer;

import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Color;
import android.graphics.SurfaceTexture;
import android.os.Build;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.WindowManager;
import android.widget.FrameLayout;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.ss.ttvideoengine.TTVideoEngine;

public class VeplayerView extends FrameLayout {
  private static final String TAG = "VeplayerView";
  private static void logD(String msg) {
    if (BuildConfig.DEBUG) {
      Log.d(TAG, msg);
    }
  }
  public String viewId;
  public String viewKind;
  public View subView;

  private final ThemedReactContext themedReactContext;
  private boolean hasRegister = false;
  private boolean hasLoad = false;
  private SurfaceTexture surfaceTexture = null;
  private TTVideoEngine ttVideoEngine = null;

  // Picture-in-Picture state
  private boolean mIsInPictureInPicture = false;
  private boolean mMovedToRootForPiP = false;
  // View reparenting for system PiP
  private View mOriginalParent;
  private ViewGroup.LayoutParams mOriginalLayoutParams;
  private final java.util.List<View> mHiddenViews = new java.util.ArrayList<>();

  public boolean isInPictureInPicture() {
    return mIsInPictureInPicture;
  }

  public VeplayerView(ThemedReactContext themedReactContext) {
    super(themedReactContext);
    this.themedReactContext = themedReactContext;
  }

  public void setViewId(String viewId) {
    if (this.viewId != null && !this.viewId.equals(viewId)) {
      com.volcengine.VolcApiEngine.view.VolcViewManager.removeViewById(this.viewId);
    }
    this.viewId = viewId;
    com.volcengine.VolcApiEngine.view.VolcViewManager.putViewById(viewId, this);
    this.hasRegister = true;
    // 如果子 View 已经准备好（hasLoad 为 true），在这里补发一次 onLoad，
    // 避免因 React Prop 调用顺序不同导致事件丢失。
    if (this.hasLoad) {
      emitOnLoad();
    }
  }

  public void setPlayerEngine(TTVideoEngine ttVideoEngine) {
    this.ttVideoEngine = ttVideoEngine;
  }

  public void setViewKind(String viewKind) {
    this.viewKind = viewKind;
    switch (viewKind) {
      case "SurfaceView": {
        SurfaceView surfaceView = new SurfaceView(themedReactContext.getReactApplicationContext());
        this.addView(surfaceView);
        this.subView = surfaceView;
        this.hasLoad = true;
        // SurfaceView 不用等其surfaceHolder可用时才能设置给播放器，播放器会自动监听surfaceHolder，也就是可以立即给SDK使用
        this.emitOnLoad();
        return;
      }
      case "TextureView": {
        TextureView textureView = new TextureView(themedReactContext.getReactApplicationContext());
        subView = textureView;
        this.addView(textureView);
        textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
          @Override
          public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
            if (VeplayerView.this.ttVideoEngine != null) {
              VeplayerView.this.ttVideoEngine.setSurface(new Surface(surfaceTexture));
            }
            setSurfaceTexture(surfaceTexture);
            hasLoad = true;
            emitOnLoad();
          }

          @Override
          public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
          }

          @Override
          public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
            return true;
          }

          @Override
          public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
          }
        });
        return;
      }
    }
  }

  public void resetSubView() {
    if (subView instanceof SurfaceView) {
      SurfaceView surface = (SurfaceView) subView;
      post(new Runnable() {
        @Override
        public void run() {
          removeView(surface);
          addView(surface);
          surface.requestLayout();
          surface.invalidate();
        }
      });
    } else if (subView instanceof TextureView) {
      TextureView texture = (TextureView) subView;
      post(new Runnable() {
        @Override
        public void run() {
          removeView(texture);
          addView(texture);
          texture.requestLayout();
          texture.invalidate();
        }
      });
    }
  }

  public void setSurfaceTexture(SurfaceTexture texture) {
    surfaceTexture = texture;
  }

  public SurfaceTexture getSurfaceTexture() {
    return surfaceTexture;
  }

  public void emitOnLoad() {
    if (hasLoad && hasRegister) {
      WritableMap event = Arguments.createMap();
      ReactContext reactContext = (ReactContext) getContext();
      reactContext
          .getJSModule(RCTEventEmitter.class)
          .receiveEvent(getId(), "vodLoad", event);
    }
  }

  @Override
  protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    if (viewId != null) {
      com.volcengine.VolcApiEngine.view.VolcViewManager.putViewById(viewId, this);
    }

    // If we're re-attaching after PiP mode has ended, ensure views are restored
    // This handles edge cases where the view might be recreated during PiP transitions
    if (mMovedToRootForPiP && !mIsInPictureInPicture) {
      logD("View re-attached after PiP, restoring views");
      post(new Runnable() {
        @Override
        public void run() {
          restoreViews();
        }
      });
    }
  }

  @Override
  protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    if (viewId != null) {
      com.volcengine.VolcApiEngine.view.VolcViewManager.removeViewById(viewId);
    }
    // Clean up PiP state if view is detached while in PiP mode
    if (mIsInPictureInPicture) {
      Activity activity = getActivity();
      if (activity != null) {
        // Avoid mutating view hierarchy while the host is detaching/measuring.
        // Post the restore to the next loop to reduce chances of FrameLayout onMeasure NPE.
        post(
            new Runnable() {
              @Override
              public void run() {
                exitPictureInPictureMode();
              }
            });
      }
      mIsInPictureInPicture = false;
    }
  }

  /**
   * Called when entering system picture-in-picture mode.
   */
  public void enterPictureInPictureMode() {
    if (mIsInPictureInPicture) {
      logD("Already in picture-in-picture mode, ignoring");
      return;
    }
    logD("Entering picture-in-picture mode");
    mIsInPictureInPicture = true;

    // Hide other views and move player to root for proper PiP display
    hideOtherViewsAndMoveToRoot();

    requestLayout();
  }

  /**
   * Called when exiting system picture-in-picture mode.
   */
  public void exitPictureInPictureMode() {
    if (!mIsInPictureInPicture) {
      logD("Not in picture-in-picture mode, ignoring");
      return;
    }
    logD("Exiting picture-in-picture mode");
    mIsInPictureInPicture = false;

    // Restore views to original layout
    restoreViews();

    requestLayout();
  }

  private Activity getActivity() {
    Context context = getContext();
    while (context instanceof ContextWrapper) {
      if (context instanceof Activity) {
        return (Activity) context;
      }
      context = ((ContextWrapper) context).getBaseContext();
    }
    return null;
  }

  /**
   * Hides other views and moves the player surface view to root for system PiP.
   * This ensures the PiP window only shows the video content without other UI elements.
   */
  public void hideOtherViewsAndMoveToRoot() {
    if (mMovedToRootForPiP) {
      logD("Already moved to root for PiP, skipping");
      return;
    }

    Activity activity = getActivity();
    if (activity == null) {
      Log.w(TAG, "Cannot move to root: activity is null");
      return;
    }

    // Get the root content view
    ViewGroup rootContent = (ViewGroup) activity.getWindow().getDecorView()
        .findViewById(android.R.id.content);

    if (rootContent == null) {
      Log.w(TAG, "Cannot move to root: root content is null");
      return;
    }

    if (subView == null) {
      Log.w(TAG, "Cannot move to root: subView is null");
      return;
    }

    // Save original parent and layout params
    if (subView.getParent() instanceof ViewGroup) {
      mOriginalParent = (ViewGroup) subView.getParent();
      mOriginalLayoutParams = subView.getLayoutParams();
      logD("Saved original parent: " + mOriginalParent.getClass().getSimpleName());
    }

    Runnable moveToRoot =
        new Runnable() {
          @Override
          public void run() {
            // Hide other views in the root content (except player view hierarchy)
            for (int i = 0; i < rootContent.getChildCount(); i++) {
              View child = rootContent.getChildAt(i);
              if (child == null) {
                continue;
              }
              if (!isViewInPlayerHierarchy(child)) {
                if (child.getVisibility() == View.VISIBLE) {
                  mHiddenViews.add(child);
                  child.setVisibility(View.GONE);
                  logD("Hidden view: " + child.getClass().getSimpleName());
                }
              }
            }

            // Remove player view from its current parent
            if (subView.getParent() instanceof ViewGroup) {
              ((ViewGroup) subView.getParent()).removeView(subView);
              logD("Removed subView from parent");
            }

            // Add player view to root with MATCH_PARENT to fill the screen
            FrameLayout.LayoutParams params =
                new FrameLayout.LayoutParams(
                    FrameLayout.LayoutParams.MATCH_PARENT,
                    FrameLayout.LayoutParams.MATCH_PARENT);
            rootContent.addView(subView, params);
            mMovedToRootForPiP = true;

            logD("Player view moved to root and other views hidden for PiP");
          }
        };

    // If root is currently in a layout pass, post the mutation to avoid transient null children during measure.
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2
        && rootContent.isInLayout()) {
      rootContent.post(moveToRoot);
    } else {
      moveToRoot.run();
    }
  }

  /**
   * Restores the view layout after exiting system PiP.
   * Moves the player view back to its original parent and shows hidden views.
   */
  public void restoreViews() {
    logD("restoreViews() called - mMovedToRootForPiP: " + mMovedToRootForPiP + ", subView: " + (subView != null));

    if (!mMovedToRootForPiP) {
      logD("Not moved to root for PiP, skipping restore");
      // Even if we didn't move to root, we might still need to restore hidden views
      // This can happen in some edge cases during PiP transitions
      if (!mHiddenViews.isEmpty()) {
        logD("Restoring hidden views anyway (count: " + mHiddenViews.size() + ")");
        for (View view : mHiddenViews) {
          view.setVisibility(View.VISIBLE);
        }
        mHiddenViews.clear();
      }
      return;
    }

    if (subView == null) {
      Log.w(TAG, "Cannot restore: subView is null");
      return;
    }

    // Log current state before restoration
    ViewParent currentParent = subView.getParent();
    logD("Current subView parent: " + (currentParent != null ? currentParent.getClass().getSimpleName() : "null"));
    logD("Original parent: " + (mOriginalParent != null ? mOriginalParent.getClass().getSimpleName() : "null"));

    // Remove player view from its current parent (should be root content)
    if (currentParent instanceof ViewGroup) {
      ViewGroup vg = (ViewGroup) currentParent;
      Runnable removeFromRoot =
          new Runnable() {
            @Override
            public void run() {
              if (subView.getParent() == vg) {
                vg.removeView(subView);
                logD("Removed subView from current parent");
              }
            }
          };
      if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2
          && vg.isInLayout()) {
        vg.post(removeFromRoot);
      } else {
        removeFromRoot.run();
      }
    }

    // First, ensure subView is added to VeplayerView (this)
    // We need to check if it's already a child to avoid "view already has parent" error
    if (subView.getParent() != this) {
      // If original parent was this VeplayerView, add directly
      if (mOriginalParent == this) {
        if (mOriginalLayoutParams != null) {
          this.addView(subView, mOriginalLayoutParams);
          logD("Added subView directly to VeplayerView with original params");
        } else {
          this.addView(subView);
          logD("Added subView directly to VeplayerView without params");
        }
      } else {
        // Otherwise add to original parent
        if (mOriginalParent instanceof ViewGroup) {
          if (mOriginalLayoutParams != null) {
            ((ViewGroup) mOriginalParent).addView(subView, mOriginalLayoutParams);
            logD("Added subView to original parent with original params");
          } else {
            ((ViewGroup) mOriginalParent).addView(subView);
            logD("Added subView to original parent without params");
          }
        }
      }
    }

    // Restore hidden views
    for (View view : mHiddenViews) {
      if (view == null) {
        continue;
      }
      view.setVisibility(View.VISIBLE);
    }
    int hiddenCount = mHiddenViews.size();
    mHiddenViews.clear();

    // Reset state
    mMovedToRootForPiP = false;
    mOriginalParent = null;
    mOriginalLayoutParams = null;

    // Request layout and invalidate to ensure proper rendering
    post(new Runnable() {
      @Override
      public void run() {
        if (subView != null) {
          subView.requestLayout();
          subView.invalidate();
        }
        requestLayout();
        invalidate();
        logD("Layout refresh requested after restore");
      }
    });

    logD("Views restored after PiP (restored " + hiddenCount + " hidden views)");
  }

  /**
   * Checks if a view is in the player view's hierarchy (to avoid hiding the player itself).
   */
  private boolean isViewInPlayerHierarchy(View view) {
    // Check if it's the subView (SurfaceView/TextureView) or this VeplayerView
    if (view == subView || view == this) {
      return true;
    }

    // Check if view is an ancestor of this VeplayerView
    ViewParent parent = this.getParent();
    while (parent instanceof View) {
      if (parent == view) {
        return true;
      }
      parent = ((View) parent).getParent();
    }

    // Check if view is a descendant of this VeplayerView
    if (view instanceof ViewGroup) {
      return isViewInSubtree((ViewGroup) view, this);
    }

    return false;
  }

  /**
   * Recursively checks if a target view is in the given subtree.
   */
  private boolean isViewInSubtree(ViewGroup root, View target) {
    for (int i = 0; i < root.getChildCount(); i++) {
      View child = root.getChildAt(i);
      if (child == target) {
        return true;
      }
      if (child instanceof ViewGroup) {
        if (isViewInSubtree((ViewGroup) child, target)) {
          return true;
        }
      }
    }
    return false;
  }
}
