package com.tencent.qian.webview.internal;

import android.Manifest;
import android.app.Activity;
import android.app.DownloadManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import android.webkit.CookieManager;
import android.webkit.DownloadListener;
import android.webkit.URLUtil;
import android.webkit.WebView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.core.util.Supplier;

import com.tencent.qian.webview.R;
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.Collections;

/**
 * 为 WebView 注册 DownloadListener。
 */
public final class DownloadListenerInstaller {

    private static final String TAG = "QianDownloadListener";

    private DownloadListenerInstaller() {}

    public static void install(
            @NonNull WebView webView,
            @NonNull Context context,
            @NonNull RuntimePermissionRequester permissionRequester,
            @NonNull Supplier<Activity> activityProvider,
            @NonNull Supplier<SignCallback> callbackProvider,
            @NonNull TencentQianWebViewConfig config
    ) {
        webView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> {
            // 安全校验
            String urlScheme = null;
            try {
                Uri parsed = Uri.parse(url);
                if (parsed.getScheme() != null) urlScheme = parsed.getScheme().toLowerCase();
            } catch (Exception ignored) {}

            if (!"http".equals(urlScheme) && !"https".equals(urlScheme)) {
                Log.w(TAG, "blocked download with non-http(s) scheme: " + urlScheme + ", url=" + url);
                SignCallback cb = callbackProvider.get();
                if (cb != null) cb.onEvent(new SdkEvent.DownloadFailed(
                        url != null ? url : "", "blocked non-http(s) scheme: " + urlScheme));
                return;
            }

            SignCallback cb = callbackProvider.get();
            if (cb != null) cb.onEvent(new SdkEvent.DownloadStart(
                    url != null ? url : "",
                    mimetype != null ? mimetype : "",
                    contentDisposition != null ? contentDisposition : ""));

            // Android 6-9: 需要 WRITE_EXTERNAL_STORAGE 权限
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
                boolean hasWritePermission = ContextCompat.checkSelfPermission(
                        context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        == PackageManager.PERMISSION_GRANTED;

                if (!hasWritePermission) {
                    Activity activity = activityProvider.get();
                    if (activity == null) {
                        fallbackToActionView(context, url, callbackProvider);
                        return;
                    }
                    permissionRequester.request(
                            activity,
                            new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                            grants -> {
                                Boolean granted = grants.get(Manifest.permission.WRITE_EXTERNAL_STORAGE);
                                if (granted != null && granted) {
                                    if (!enqueueDownload(context, url, userAgent, contentDisposition, mimetype, config.isDownloadWithCookies())) {
                                        fallbackToActionView(context, url, callbackProvider);
                                    }
                                } else {
                                    SignCallback cb2 = callbackProvider.get();
                                    if (cb2 != null) cb2.onEvent(new SdkEvent.PermissionDenied(
                                            Collections.singletonList(Manifest.permission.WRITE_EXTERNAL_STORAGE)));
                                    fallbackToActionView(context, url, callbackProvider);
                                }
                            });
                    return;
                }
            }

            if (enqueueDownload(context, url, userAgent, contentDisposition, mimetype, config.isDownloadWithCookies())) {
                return;
            }
            fallbackToActionView(context, url, callbackProvider);
        });
    }

    private static boolean enqueueDownload(
            @NonNull Context context,
            @Nullable String url,
            @Nullable String userAgent,
            @Nullable String contentDisposition,
            @Nullable String mimetype,
            boolean downloadWithCookies
    ) {
        try {
            DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
            if (dm == null) return false;

            String fileName = URLUtil.guessFileName(url, contentDisposition, mimetype);
            DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));

            if (mimetype != null && !mimetype.trim().isEmpty()) {
                request.setMimeType(mimetype);
            }
            if (userAgent != null && !userAgent.trim().isEmpty()) {
                request.addRequestHeader("User-Agent", userAgent);
            }
            if (downloadWithCookies) {
                String cookie = CookieManager.getInstance().getCookie(url);
                if (cookie != null && !cookie.trim().isEmpty()) {
                    request.addRequestHeader("Cookie", cookie);
                }
            }
            request.setTitle(fileName);
            request.setDescription(context.getString(R.string.qian_downloading));
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
            request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);

            dm.enqueue(request);
            return true;
        } catch (Exception e) {
            Log.w(TAG, "DownloadManager.enqueue() failed, will fallback", e);
            return false;
        }
    }

    private static void fallbackToActionView(
            @NonNull Context context,
            @Nullable String url,
            @NonNull Supplier<SignCallback> callbackProvider
    ) {
        try {
            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
        } catch (ActivityNotFoundException e) {
            Log.w(TAG, "no Activity to handle download url=" + url, e);
            SignCallback cb = callbackProvider.get();
            if (cb != null) cb.onEvent(new SdkEvent.DownloadFailed(
                    url != null ? url : "", "ActivityNotFoundException"));
        } catch (Exception e) {
            Log.w(TAG, "unexpected exception starting download url=" + url, e);
            SignCallback cb = callbackProvider.get();
            if (cb != null) cb.onEvent(new SdkEvent.DownloadFailed(
                    url != null ? url : "", e.getClass().getSimpleName()));
        }
    }
}
