package com.tencent.qian.webview.chrome;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.net.Uri;
import android.webkit.JsResult;
import android.webkit.PermissionRequest;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Consumer;
import androidx.core.util.Supplier;

import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.tencent.qian.webview.R;
import com.tencent.qian.webview.filechooser.FileChooserLauncher;
import com.tencent.qian.webview.model.SdkEvent;
import com.tencent.qian.webview.model.SignCallback;
import com.tencent.qian.webview.model.TencentQianWebViewConfig;
import com.tencent.qian.webview.permission.RuntimePermissionRequester;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;

/**
 * SDK 内置 WebChromeClient。
 */
public class QianWebChromeClient extends WebChromeClient {

    private final TencentQianWebViewConfig config;
    private final RuntimePermissionRequester permissionRequester;
    private final FileChooserLauncher fileChooserLauncher;
    private final Supplier<SignCallback> callbackProvider;
    private final Supplier<Activity> activityProvider;
    @Nullable private final Consumer<Integer> progressListener;

    public QianWebChromeClient(
            @NonNull TencentQianWebViewConfig config,
            @NonNull RuntimePermissionRequester permissionRequester,
            @NonNull FileChooserLauncher fileChooserLauncher,
            @NonNull Supplier<SignCallback> callbackProvider,
            @NonNull Supplier<Activity> activityProvider,
            @Nullable Consumer<Integer> progressListener
    ) {
        this.config = config;
        this.permissionRequester = permissionRequester;
        this.fileChooserLauncher = fileChooserLauncher;
        this.callbackProvider = callbackProvider;
        this.activityProvider = activityProvider;
        this.progressListener = progressListener;
    }

    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        if (progressListener != null) progressListener.accept(newProgress);
    }

    @Override
    public void onPermissionRequest(PermissionRequest request) {
        if (request == null) return;
        Activity activity = activityProvider.get();
        if (activity == null) { request.deny(); return; }

        LinkedHashSet<String> androidPermsSet = new LinkedHashSet<>();
        for (String webPerm : request.getResources()) {
            if (PermissionRequest.RESOURCE_VIDEO_CAPTURE.equals(webPerm)) {
                androidPermsSet.add(Manifest.permission.CAMERA);
            } else if (PermissionRequest.RESOURCE_AUDIO_CAPTURE.equals(webPerm)) {
                androidPermsSet.add(Manifest.permission.RECORD_AUDIO);
            }
        }

        if (androidPermsSet.isEmpty()) { request.deny(); return; }

        String[] androidPerms = androidPermsSet.toArray(new String[0]);
        permissionRequester.request(activity, androidPerms, grants -> {
            boolean allGranted = true;
            for (Boolean v : grants.values()) { if (!v) { allGranted = false; break; } }
            if (allGranted) {
                request.grant(request.getResources());
                SignCallback cb = callbackProvider.get();
                if (cb != null) cb.onEvent(new SdkEvent.PermissionGranted(new ArrayList<>(grants.keySet())));
            } else {
                request.deny();
                List<String> denied = new ArrayList<>();
                for (Map.Entry<String, Boolean> e : grants.entrySet()) {
                    if (!e.getValue()) denied.add(e.getKey());
                }
                SignCallback cb = callbackProvider.get();
                if (cb != null) cb.onEvent(new SdkEvent.PermissionDenied(denied));
            }
        });
    }

    @Override
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
        Activity activity = activityProvider.get();
        if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
            if (result != null) result.confirm();
            return true;
        }
        String msg = message != null ? message : "";
        showAlert(activity, msg, activity.getString(R.string.qian_js_ok), () -> { if (result != null) result.confirm(); });
        return true;
    }

    @Override
    public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
        Activity activity = activityProvider.get();
        if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
            if (result != null) result.cancel();
            return true;
        }
        String msg = message != null ? message : "";
        showConfirm(activity, msg,
                () -> { if (result != null) result.confirm(); },
                () -> { if (result != null) result.cancel(); });
        return true;
    }

    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
        if (fileChooserParams == null) return false;
        return fileChooserLauncher.launch(
                fileChooserParams,
                filePathCallback,
                config.getLegacyFaceCaptureMimeTypes(),
                config.getCameraCaptureBehavior()
        );
    }

    private void showAlert(@NonNull Context ctx, @NonNull String message, @NonNull String positive, @NonNull Runnable onPositive) {
        try {
            if (config.getJsDialogStyle() == TencentQianWebViewConfig.JsDialogStyle.MATERIAL) {
                new MaterialAlertDialogBuilder(ctx)
                        .setMessage(message)
                        .setPositiveButton(positive, (d, w) -> { d.dismiss(); onPositive.run(); })
                        .setOnCancelListener(d -> onPositive.run())
                        .setCancelable(true)
                        .show();
            } else {
                new android.app.AlertDialog.Builder(ctx)
                        .setMessage(message)
                        .setPositiveButton(positive, (d, w) -> { d.dismiss(); onPositive.run(); })
                        .setOnCancelListener(d -> onPositive.run())
                        .setCancelable(true)
                        .show();
            }
        } catch (android.view.WindowManager.BadTokenException ignored) {
            onPositive.run();
        }
    }

    private void showConfirm(@NonNull Context ctx, @NonNull String message, @NonNull Runnable onPositive, @NonNull Runnable onNegative) {
        try {
            String ok = ctx.getString(R.string.qian_js_ok);
            String cancel = ctx.getString(R.string.qian_js_cancel);
            if (config.getJsDialogStyle() == TencentQianWebViewConfig.JsDialogStyle.MATERIAL) {
                new MaterialAlertDialogBuilder(ctx)
                        .setMessage(message)
                        .setPositiveButton(ok, (d, w) -> { d.dismiss(); onPositive.run(); })
                        .setNegativeButton(cancel, (d, w) -> { d.dismiss(); onNegative.run(); })
                        .setOnCancelListener(d -> onNegative.run())
                        .setCancelable(true)
                        .show();
            } else {
                new android.app.AlertDialog.Builder(ctx)
                        .setMessage(message)
                        .setPositiveButton(ok, (d, w) -> { d.dismiss(); onPositive.run(); })
                        .setNegativeButton(cancel, (d, w) -> { d.dismiss(); onNegative.run(); })
                        .setOnCancelListener(d -> onNegative.run())
                        .setCancelable(true)
                        .show();
            }
        } catch (android.view.WindowManager.BadTokenException ignored) {
            onNegative.run();
        }
    }
}
