package com.termux.shared.interact;

import android.Manifest;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import androidx.appcompat.app.AppCompatActivity;
import com.termux.shared.R;
import com.termux.shared.data.DataUtils;
import com.termux.shared.data.IntentUtils;
import com.termux.shared.file.FileUtils;
import com.termux.shared.logger.Logger;
import com.termux.shared.errors.Error;
import com.termux.shared.android.PermissionUtils;
import java.nio.charset.Charset;
import javax.annotation.Nullable;

public class ShareUtils {

    private static final String LOG_TAG = "ShareUtils";

    /**
     * Open the system app chooser that allows the user to select which app to send the intent.
     *
     * @param context The context for operations.
     * @param intent The intent that describes the choices that should be shown.
     * @param title The title for choose menu.
     */
    public static void openSystemAppChooser(final Context context, final Intent intent, final String title) {
        if (context == null)
            return;
        final Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
        chooserIntent.putExtra(Intent.EXTRA_INTENT, intent);
        chooserIntent.putExtra(Intent.EXTRA_TITLE, title);
        chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            context.startActivity(chooserIntent);
        } catch (Exception e) {
            Logger.logStackTraceWithMessage(LOG_TAG, "Failed to open system chooser for:\n" + IntentUtils.getIntentString(chooserIntent), e);
        }
    }

    /**
     * Share text.
     *
     * @param context The context for operations.
     * @param subject The subject for sharing.
     * @param text The text to share.
     */
    public static void shareText(final Context context, final String subject, final String text) {
        shareText(context, subject, text, null);
    }

    /**
     * Share text.
     *
     * @param context The context for operations.
     * @param subject The subject for sharing.
     * @param text The text to share.
     * @param title The title for share menu.
     */
    public static void shareText(final Context context, final String subject, final String text, @Nullable final String title) {
        if (context == null || text == null)
            return;
        final Intent shareTextIntent = new Intent(Intent.ACTION_SEND);
        shareTextIntent.setType("text/plain");
        shareTextIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
        shareTextIntent.putExtra(Intent.EXTRA_TEXT, DataUtils.getTruncatedCommandOutput(text, DataUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES, true, false, false));
        openSystemAppChooser(context, shareTextIntent, DataUtils.isNullOrEmpty(title) ? context.getString(R.string.title_share_with) : title);
    }

    /**
     * Wrapper for {@link #copyTextToClipboard(Context, String, String, String)} with `null` `clipDataLabel` and `toastString`.
     */
    public static void copyTextToClipboard(Context context, final String text) {
        copyTextToClipboard(context, null, text, null);
    }

    /**
     * Wrapper for {@link #copyTextToClipboard(Context, String, String, String)} with `null` `clipDataLabel`.
     */
    public static void copyTextToClipboard(Context context, final String text, final String toastString) {
        copyTextToClipboard(context, null, text, toastString);
    }

    /**
     * Copy the text to primary clip of the clipboard.
     *
     * @param context The context for operations.
     * @param clipDataLabel The label to show to the user describing the copied text.
     * @param text The text to copy.
     * @param toastString If this is not {@code null} or empty, then a toast is shown if copying to
     *                    clipboard is successful.
     */
    public static void copyTextToClipboard(Context context, @Nullable final String clipDataLabel, final String text, final String toastString) {
        if (context == null || text == null)
            return;
        ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
        if (clipboardManager == null)
            return;
        clipboardManager.setPrimaryClip(ClipData.newPlainText(clipDataLabel, DataUtils.getTruncatedCommandOutput(text, DataUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES, true, false, false)));
        if (toastString != null && !toastString.isEmpty())
            Logger.showToast(context, toastString, true);
    }

    /**
     * Wrapper for {@link #getTextFromClipboard(Context, boolean)} that returns primary text {@link String}
     * if its set and not empty.
     */
    @Nullable
    public static String getTextStringFromClipboardIfSet(Context context, boolean coerceToText) {
        CharSequence textCharSequence = getTextFromClipboard(context, coerceToText);
        if (textCharSequence == null)
            return null;
        String textString = textCharSequence.toString();
        return !textString.isEmpty() ? textString : null;
    }

    /**
     * Get the text from primary clip of the clipboard.
     *
     * @param context The context for operations.
     * @param coerceToText Whether to call {@link ClipData.Item#coerceToText(Context)} to coerce
     *                     non-text data to text.
     * @return Returns the {@link CharSequence} of primary text. This will be `null` if failed to get it.
     */
    @Nullable
    public static CharSequence getTextFromClipboard(Context context, boolean coerceToText) {
        if (context == null)
            return null;
        ClipboardManager clipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
        if (clipboardManager == null)
            return null;
        ClipData clipData = clipboardManager.getPrimaryClip();
        if (clipData == null)
            return null;
        ClipData.Item clipItem = clipData.getItemAt(0);
        if (clipItem == null)
            return null;
        return coerceToText ? clipItem.coerceToText(context) : clipItem.getText();
    }

    /**
     * Open a url.
     *
     * @param context The context for operations.
     * @param url The url to open.
     */
    public static void openUrl(final Context context, final String url) {
        if (context == null || url == null || url.isEmpty())
            return;
        Uri uri = Uri.parse(url);
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        try {
            context.startActivity(intent);
        } catch (ActivityNotFoundException e) {
            // If no activity found to handle intent, show system chooser
            openSystemAppChooser(context, intent, context.getString(R.string.title_open_url_with));
        } catch (Exception e) {
            Logger.logStackTraceWithMessage(LOG_TAG, "Failed to open url \"" + url + "\"", e);
        }
    }

    /**
     * Save a file at the path.
     *
     * If if path is under {@link Environment#getExternalStorageDirectory()}
     * or `/sdcard` and storage permission is missing, it will be requested if {@code context} is an
     * instance of {@link Activity} or {@link AppCompatActivity} and {@code storagePermissionRequestCode}
     * is `>=0` and the function will automatically return. The caller should call this function again
     * if user granted the permission.
     *
     * @param context The context for operations.
     * @param label The label for file.
     * @param filePath The path to save the file.
     * @param text The text to write to file.
     * @param showToast If set to {@code true}, then a toast is shown if saving to file is successful.
     * @param storagePermissionRequestCode The request code to use while asking for permission.
     */
    public static void saveTextToFile(final Context context, final String label, final String filePath, final String text, final boolean showToast, final int storagePermissionRequestCode) {
        if (context == null || filePath == null || filePath.isEmpty() || text == null)
            return;
        // If path is under primary external storage directory, then check for missing permissions.
        if ((FileUtils.isPathInDirPath(filePath, Environment.getExternalStorageDirectory().getAbsolutePath(), true) || FileUtils.isPathInDirPath(filePath, "/sdcard", true)) && !PermissionUtils.checkPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            Logger.logErrorAndShowToast(context, LOG_TAG, context.getString(R.string.msg_storage_permission_not_granted));
            if (storagePermissionRequestCode >= 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (context instanceof AppCompatActivity)
                    PermissionUtils.requestPermission(((AppCompatActivity) context), Manifest.permission.WRITE_EXTERNAL_STORAGE, storagePermissionRequestCode);
                else if (context instanceof Activity)
                    PermissionUtils.requestPermission(((Activity) context), Manifest.permission.WRITE_EXTERNAL_STORAGE, storagePermissionRequestCode);
            }
            return;
        }
        Error error = FileUtils.writeTextToFile(label, filePath, Charset.defaultCharset(), text, false);
        if (error != null) {
            Logger.logErrorExtended(LOG_TAG, error.toString());
            Logger.showToast(context, Error.getMinimalErrorString(error), true);
        } else {
            if (showToast)
                Logger.showToast(context, context.getString(R.string.msg_file_saved_successfully, label, filePath), true);
        }
    }
}
