/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 * <p/>
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
 * copy, modify, and distribute this software in source code or binary form for use
 * in connection with the web services and APIs provided by Facebook.
 * <p/>
 * As with any software that integrates with the Facebook platform, your use of
 * this software is subject to the Facebook Developer Principles and Policies
 * [http://developers.facebook.com/policy/]. This copyright notice shall be
 * included in all copies or substantial portions of the software.
 * <p/>
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package com.facebook.reactnative.androidsdk;

import android.net.Uri;

import com.facebook.AccessToken;
import com.facebook.AccessTokenSource;
import com.facebook.Profile;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.share.model.GameRequestContent;
import com.facebook.share.model.ShareContent;
import com.facebook.share.model.ShareLinkContent;
import com.facebook.share.model.ShareHashtag;
import com.facebook.share.model.SharePhoto;
import com.facebook.share.model.SharePhotoContent;
import com.facebook.share.model.ShareVideo;
import com.facebook.share.model.ShareVideoContent;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import androidx.annotation.Nullable;


/**
 * This class is solely for the use of other packages within the React Native Facebook SDK for Android.
 * Use of any of the classes in this package is unsupported, and they may be modified or removed
 * without warning at any time.
 */
public final class Utility {

    public static AccessToken buildAccessToken(ReadableMap accessTokenMap) {
        return new AccessToken(
                accessTokenMap.getString("accessToken"),
                accessTokenMap.getString("applicationID"),
                accessTokenMap.getString("userID"),
                accessTokenMap.hasKey("permissions") && !accessTokenMap.isNull("permissions") ? reactArrayToStringList(accessTokenMap.getArray("permissions")) : null,
                accessTokenMap.hasKey("declinedPermissions") && !accessTokenMap.isNull("declinedPermissions") ? reactArrayToStringList(accessTokenMap.getArray("declinedPermissions")) : null,
                accessTokenMap.hasKey("expiredPermissions") && !accessTokenMap.isNull("expiredPermissions") ? reactArrayToStringList(accessTokenMap.getArray("expiredPermissions")) : null,
                accessTokenMap.hasKey("accessTokenSource") && !accessTokenMap.isNull("accessTokenSource") ? AccessTokenSource.valueOf(accessTokenMap.getString("accessTokenSource")) : null,
                accessTokenMap.hasKey("expirationTime") && !accessTokenMap.isNull("expirationTime") ? new Date((long) accessTokenMap.getDouble("expirationTime")) : null,
                accessTokenMap.hasKey("lastRefreshTime") && !accessTokenMap.isNull("lastRefreshTime") ? new Date((long) accessTokenMap.getDouble("lastRefreshTime")) : null,
                accessTokenMap.hasKey("dataAccessExpirationTime") && !accessTokenMap.isNull("dataAccessExpirationTime") ? new Date((long) accessTokenMap.getDouble("dataAccessExpirationTime")) : null
        );
    }

    public static WritableMap accessTokenToReactMap(AccessToken accessToken) {
        WritableMap map = Arguments.createMap();
        map.putString("accessToken", accessToken.getToken());
        map.putString("applicationID", accessToken.getApplicationId());
        map.putString("userID", accessToken.getUserId());
        map.putArray(
            "permissions",
            Arguments.fromJavaArgs(setToStringArray(accessToken.getPermissions())));
        map.putArray(
            "declinedPermissions",
            Arguments.fromJavaArgs(setToStringArray(accessToken.getDeclinedPermissions())));
        map.putArray(
            "expiredPermissions",
            Arguments.fromJavaArgs(setToStringArray(accessToken.getExpiredPermissions())));
        map.putString("accessTokenSource", accessToken.getSource().name());
        map.putDouble("expirationTime", (double) accessToken.getExpires().getTime());
        map.putDouble("lastRefreshTime", (double) accessToken.getLastRefresh().getTime());
        map.putDouble("dataAccessExpirationTime", (double) accessToken.getDataAccessExpirationTime().getTime());
        return map;
    }

    public static WritableMap profileToReactMap(Profile profile) {
        WritableMap map = Arguments.createMap();

        String name = profile.getName();
        Utility.putStringOrNull(map, "name", name);

        String firstName = profile.getFirstName();
        Utility.putStringOrNull(map, "firstName", firstName);

        String lastName = profile.getLastName();
        Utility.putStringOrNull(map, "lastName", lastName);

        String middleName = profile.getMiddleName();
        Utility.putStringOrNull(map, "middleName", middleName);

        Uri profilePictureUri = profile.getProfilePictureUri(100, 100);
        Utility.putStringOrNull(map, "imageURL", profilePictureUri.toString());

        Uri linkUri = profile.getLinkUri();
        Utility.putStringOrNull(map, "linkURL", linkUri.toString());

        String userId = profile.getId();
        Utility.putStringOrNull(map, "userID", userId);

        return map;
    }

    public static ShareContent buildShareContent(ReadableMap shareContentMap) {
        ShareContent shareContent = null;
        if (shareContentMap != null) {
            String contentType = shareContentMap.getString("contentType");
            if (contentType.equals("link")) {
                shareContent = buildShareLinkContent(shareContentMap);
            } else if (contentType.equals("photo")) {
                shareContent = buildSharePhotoContent(shareContentMap);
            } else if (contentType.equals("video")) {
                shareContent = buildShareVideoContent(shareContentMap);
            }
        }
        return shareContent;
    }

    public static GameRequestContent buildGameRequestContent(ReadableMap gameRequestContentMap) {
        GameRequestContent.Builder gameRequestContentBuilder = new GameRequestContent.Builder();
        String actionType = getValueOrNull(gameRequestContentMap, "actionType");
        if (actionType != null) {
            gameRequestContentBuilder.setActionType(
                    GameRequestContent.ActionType.valueOf(actionType.toUpperCase(Locale.ROOT)));
        }
        String filters = getValueOrNull(gameRequestContentMap, "filters");
        if (filters != null) {
            gameRequestContentBuilder.setFilters(
                    GameRequestContent.Filters.valueOf(filters.toUpperCase(Locale.ROOT)));
        }
        gameRequestContentBuilder.setMessage(gameRequestContentMap.getString("message"));
        if (gameRequestContentMap.hasKey("recipients")) {
            gameRequestContentBuilder.setRecipients(
                    reactArrayToStringList(gameRequestContentMap.getArray("recipients")));
        }
        gameRequestContentBuilder.setTitle(getValueOrNull(gameRequestContentMap, "title"));
        gameRequestContentBuilder.setData(getValueOrNull(gameRequestContentMap, "data"));
        gameRequestContentBuilder.setObjectId(getValueOrNull(gameRequestContentMap, "objectId"));
        if (gameRequestContentMap.hasKey("suggestions")) {
            gameRequestContentBuilder.setSuggestions(reactArrayToStringList(
                    gameRequestContentMap.getArray("suggestions")));
        }
        return gameRequestContentBuilder.build();
    }

    public static ShareLinkContent buildShareLinkContent(ReadableMap shareLinkContent) {
        ShareLinkContent.Builder contentBuilder = new ShareLinkContent.Builder();
        contentBuilder.setContentUrl(Uri.parse(shareLinkContent.getString("contentUrl")));
        contentBuilder.setQuote(getValueOrNull(shareLinkContent, "quote"));
        appendGenericContent(contentBuilder, shareLinkContent);
        return contentBuilder.build();
    }

    public static SharePhotoContent buildSharePhotoContent(ReadableMap sharePhotoContent) {
        SharePhotoContent.Builder contentBuilder = new SharePhotoContent.Builder();
        contentBuilder.setPhotos(reactArrayToPhotoList(sharePhotoContent.getArray("photos")));
        String url = getValueOrNull(sharePhotoContent, "contentUrl");
        contentBuilder.setContentUrl(url != null ? Uri.parse(url) : null);
        appendGenericContent(contentBuilder, sharePhotoContent);
        return contentBuilder.build();
    }

    public static SharePhoto buildSharePhoto(ReadableMap photoMap) {
        SharePhoto.Builder photoBuilder = new SharePhoto.Builder();
        photoBuilder.setImageUrl(Uri.parse(photoMap.getString("imageUrl")));
        photoBuilder.setCaption(getValueOrNull(photoMap, "caption"));
        if (photoMap.hasKey("userGenerated")) {
            photoBuilder.setUserGenerated(photoMap.getBoolean("userGenerated"));
        }
        return photoBuilder.build();
    }

    public static ShareContent buildShareVideoContent(ReadableMap shareVideoContent) {
        ShareVideoContent.Builder contentBuilder = new ShareVideoContent.Builder();
        String url = getValueOrNull(shareVideoContent, "contentUrl");
        contentBuilder.setContentUrl(url != null ? Uri.parse(url) : null);
        contentBuilder.setContentDescription(
                getValueOrNull(shareVideoContent, "contentDescription"));
        contentBuilder.setContentTitle(getValueOrNull(shareVideoContent, "contentTitle"));
        if (shareVideoContent.hasKey("previewPhoto")) {
            contentBuilder.setPreviewPhoto(buildSharePhoto(shareVideoContent.getMap("previewPhoto")));
        }
        if (shareVideoContent.hasKey("video")) {
            contentBuilder.setVideo(buildShareVideo(shareVideoContent.getMap("video")));
        }
        appendGenericContent(contentBuilder, shareVideoContent);
        return contentBuilder.build();
    }

    private static void appendGenericContent(ShareContent.Builder contentBuilder, ReadableMap shareContent) {
        if (shareContent.hasKey("commonParameters")) {
            ReadableMap commonParameters = shareContent.getMap("commonParameters");
            contentBuilder.setPeopleIds(
                    commonParameters.hasKey("peopleIds")
                            ? reactArrayToStringList(commonParameters.getArray("peopleIds"))
                            : null);
            contentBuilder.setPlaceId(getValueOrNull(commonParameters, "placeId"));
            contentBuilder.setRef(getValueOrNull(commonParameters, "ref"));
            if (commonParameters.hasKey("hashtag")) {
                ShareHashtag tag = new ShareHashtag.Builder().setHashtag(commonParameters.getString("hashtag")).build();
                contentBuilder.setShareHashtag(tag);
            }
        }
    }

    public static ShareVideo buildShareVideo(ReadableMap videoMap) {
        ShareVideo.Builder videoBuilder = new ShareVideo.Builder();
        if (videoMap.hasKey("localUrl")) {
            videoBuilder.setLocalUrl(Uri.parse(videoMap.getString("localUrl")));
        }
        return videoBuilder.build();
    }

    public static List<SharePhoto> reactArrayToPhotoList(ReadableArray photos) {
        List<SharePhoto> list = new ArrayList<>(photos.size());
        for (int i = 0; i < photos.size(); i++) {
            ReadableMap photoDetail = photos.getMap(i);
            list.add(buildSharePhoto(photoDetail));
        }
        return list;
    }

    public static String getValueOrNull(ReadableMap map, String key) {
        if (map.hasKey(key)) {
            return map.getString(key);
        }
        return null;
    }

    public static @Nullable List<String> reactArrayToStringList(@Nullable ReadableArray array) {
        if (array == null) {
            return null;
        }
        List<String> list = new ArrayList<>(array.size());
        for (int i = 0; i < array.size(); i++) {
            list.add(array.getString(i));
        }
        return list;
    }

    public static WritableArray listToReactArray(List<String> list) {
        WritableArray array = Arguments.createArray();
        for (String e: list) {
            array.pushString(e);
        }
        return array;
    }

    public static String[] setToStringArray(Set<String> set) {
        String[] array = new String[set.size()];
        int i = 0;
        for (String e : set) {
            array[i++] = e;
        }
        return array;
    }

    private static void putStringOrNull(WritableMap map, String key, String value) {
        if (value == null) {
            map.putNull(key);
        } else {
            map.putString(key, value);
        }
    }
}
