/*
 * 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.modules.share;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import android.app.Activity;
import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.JavaOnlyMap;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactTestHelper;
import com.facebook.react.bridge.WritableMap;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.internal.ShadowExtractor;
import org.robolectric.shadows.ShadowApplication;

@PrepareForTest({Arguments.class})
@RunWith(RobolectricTestRunner.class)
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "androidx.*", "android.*"})
public class ShareModuleTest {

  private Activity mActivity;
  private ShareModule mShareModule;

  @Rule public PowerMockRule rule = new PowerMockRule();

  @Before
  public void prepareModules() throws Exception {
    PowerMockito.mockStatic(Arguments.class);
    Mockito.when(Arguments.createMap())
        .thenAnswer(
            new Answer<Object>() {
              @Override
              public Object answer(InvocationOnMock invocation) throws Throwable {
                return new JavaOnlyMap();
              }
            });

    mShareModule = new ShareModule(ReactTestHelper.createCatalystContextForTest());
  }

  @After
  public void cleanUp() {
    mActivity = null;
    mShareModule = null;
  }

  @Test
  public void testShareDialog() {
    final String title = "Title";
    final String message = "Message";
    final String dialogTitle = "Dialog Title";

    JavaOnlyMap content = new JavaOnlyMap();
    content.putString("title", title);
    content.putString("message", message);

    final SimplePromise promise = new SimplePromise();

    mShareModule.share(content, dialogTitle, promise);

    final Intent chooserIntent =
        ((ShadowApplication) ShadowExtractor.extract(RuntimeEnvironment.application))
            .getNextStartedActivity();
    assertNotNull("Dialog was not displayed", chooserIntent);
    assertEquals(Intent.ACTION_CHOOSER, chooserIntent.getAction());
    assertEquals(dialogTitle, chooserIntent.getExtras().get(Intent.EXTRA_TITLE));

    final Intent contentIntent = (Intent) chooserIntent.getExtras().get(Intent.EXTRA_INTENT);
    assertNotNull("Intent was not built correctly", contentIntent);
    assertEquals(Intent.ACTION_SEND, contentIntent.getAction());
    assertEquals(title, contentIntent.getExtras().get(Intent.EXTRA_SUBJECT));
    assertEquals(message, contentIntent.getExtras().get(Intent.EXTRA_TEXT));

    assertEquals(1, promise.getResolved());
  }

  @Test
  public void testInvalidContent() {
    final String dialogTitle = "Dialog Title";

    final SimplePromise promise = new SimplePromise();

    mShareModule.share(null, dialogTitle, promise);

    assertEquals(1, promise.getRejected());
    assertEquals(ShareModule.ERROR_INVALID_CONTENT, promise.getErrorCode());
  }

  static final class SimplePromise implements Promise {
    private static final String ERROR_DEFAULT_CODE = "EUNSPECIFIED";
    private static final String ERROR_DEFAULT_MESSAGE = "Error not specified.";

    private int mResolved;
    private int mRejected;
    private Object mValue;
    private String mErrorCode;
    private String mErrorMessage;

    public int getResolved() {
      return mResolved;
    }

    public int getRejected() {
      return mRejected;
    }

    public Object getValue() {
      return mValue;
    }

    public String getErrorCode() {
      return mErrorCode;
    }

    public String getErrorMessage() {
      return mErrorMessage;
    }

    @Override
    public void resolve(Object value) {
      mResolved++;
      mValue = value;
    }

    @Override
    public void reject(String code, String message) {
      reject(code, message, /*Throwable*/ null, /*WritableMap*/ null);
    }

    @Override
    public void reject(String code, Throwable throwable) {
      reject(code, /*Message*/ null, throwable, /*WritableMap*/ null);
    }

    @Override
    public void reject(String code, String message, Throwable throwable) {
      reject(code, message, throwable, /*WritableMap*/ null);
    }

    @Override
    public void reject(Throwable throwable) {
      reject(/*Code*/ null, /*Message*/ null, throwable, /*WritableMap*/ null);
    }

    @Override
    public void reject(Throwable throwable, WritableMap userInfo) {
      reject(/*Code*/ null, /*Message*/ null, throwable, userInfo);
    }

    @Override
    public void reject(String code, @NonNull WritableMap userInfo) {
      reject(code, /*Message*/ null, /*Throwable*/ null, userInfo);
    }

    @Override
    public void reject(String code, Throwable throwable, WritableMap userInfo) {
      reject(code, /*Message*/ null, throwable, userInfo);
    }

    @Override
    public void reject(String code, String message, @NonNull WritableMap userInfo) {
      reject(code, message, /*Throwable*/ null, userInfo);
    }

    @Override
    public void reject(
        String code,
        String message,
        @Nullable Throwable throwable,
        @Nullable WritableMap userInfo) {
      mRejected++;

      if (code == null) {
        mErrorCode = ERROR_DEFAULT_CODE;
      } else {
        mErrorCode = code;
      }

      if (message != null) {
        mErrorMessage = message;
      } else if (throwable != null) {
        mErrorMessage = throwable.getMessage();
      } else {
        mErrorMessage = ERROR_DEFAULT_MESSAGE;
      }
    }

    @Override
    @Deprecated
    public void reject(String message) {
      reject(/*Code*/ null, message, /*Throwable*/ null, /*WritableMap*/ null);
    }
  }
}
