package com.sheanpackaging.mauzotrack;

import android.app.Activity;
import android.app.DownloadManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.net.DhcpInfo;
import android.net.Uri;
import android.net.http.SslError;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.text.format.Formatter;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.DownloadListener;
import android.webkit.JavascriptInterface;
import android.webkit.RenderProcessGoneDetail;
import android.webkit.ServiceWorkerClient;
import android.webkit.ServiceWorkerController;
import android.webkit.SslErrorHandler;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import org.json.JSONObject;

import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MainActivity extends Activity {
    private static final String VERSION = "1.0";
    private static final String PRODUCTION_ORIGIN = "https://app.sheanpackaging.com";
    private static final String PREFERENCES = "mauzotrack_connection";
    private static final String SERVER_KEY = "server_origin";
    private static final int FILE_CHOOSER_REQUEST = 5102;
    private static final int CONNECT_TIMEOUT_MS = 1800;

    private final Handler mainHandler = new Handler(Looper.getMainLooper());
    private final ExecutorService connectionExecutor = Executors.newSingleThreadExecutor();
    private WebView webView;
    private ValueCallback<Uri[]> fileChooserCallback;
    private volatile boolean connectionAttemptInProgress = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setStatusBarColor(Color.WHITE);
        getWindow().setNavigationBarColor(Color.WHITE);
        getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);

        webView = new WebView(this);
        webView.setBackgroundColor(Color.rgb(248, 250, 252));
        setContentView(webView);
        configureWebView();
        webView.loadUrl("file:///android_asset/loading.html");
        openIntent(getIntent());
    }

    private void configureWebView() {
        WebView.setWebContentsDebuggingEnabled(false);
        WebSettings settings = webView.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setDomStorageEnabled(true);
        settings.setDatabaseEnabled(true);
        settings.setCacheMode(WebSettings.LOAD_DEFAULT);
        settings.setAllowFileAccess(true);
        settings.setAllowFileAccessFromFileURLs(false);
        settings.setAllowUniversalAccessFromFileURLs(false);
        settings.setAllowContentAccess(true);
        settings.setBuiltInZoomControls(false);
        settings.setDisplayZoomControls(false);
        // Keep CSS text sizing aligned with Chrome. MauzoTrack's own five-step
        // text control is the single source of scaling inside the native app.
        settings.setTextZoom(100);
        settings.setUseWideViewPort(false);
        settings.setLoadWithOverviewMode(false);
        settings.setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
        settings.setUserAgentString(settings.getUserAgentString() + " MauzoTrackNative/Android/" + VERSION);

        CookieManager cookies = CookieManager.getInstance();
        cookies.setAcceptCookie(true);
        cookies.setAcceptThirdPartyCookies(webView, false);
        webView.addJavascriptInterface(new NativeBridge(), "MauzoTrackNative");

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
            ServiceWorkerController.getInstance().setServiceWorkerClient(new ServiceWorkerClient() {
                @Override
                public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
                    return null;
                }
            });
        }

        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                Uri uri = request.getUrl();
                if (isAllowedAppUri(uri)) return false;
                openExternal(uri);
                return true;
            }

            @Override
            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
                if (request.isForMainFrame() && !"file".equalsIgnoreCase(request.getUrl().getScheme())) showOffline();
            }

            @Override
            public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse response) {
                if (request.isForMainFrame() && response.getStatusCode() >= 500) showOffline();
            }

            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                handler.cancel();
                showOffline();
            }

            @Override
            public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
                mainHandler.post(() -> recreate());
                return true;
            }
        });

        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onShowFileChooser(WebView view, ValueCallback<Uri[]> callback, FileChooserParams params) {
                if (fileChooserCallback != null) fileChooserCallback.onReceiveValue(null);
                fileChooserCallback = callback;
                try {
                    startActivityForResult(params.createIntent(), FILE_CHOOSER_REQUEST);
                    return true;
                } catch (ActivityNotFoundException exception) {
                    fileChooserCallback = null;
                    return false;
                }
            }
        });

        webView.setDownloadListener(createDownloadListener());
    }

    private DownloadListener createDownloadListener() {
        return (url, userAgent, contentDisposition, mimeType, contentLength) -> {
            try {
                DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
                request.addRequestHeader("Cookie", CookieManager.getInstance().getCookie(url));
                request.addRequestHeader("User-Agent", userAgent);
                request.setMimeType(mimeType);
                String fileName = android.webkit.URLUtil.guessFileName(url, contentDisposition, mimeType);
                request.setTitle(fileName);
                request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
                request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
                ((DownloadManager) getSystemService(DOWNLOAD_SERVICE)).enqueue(request);
            } catch (Exception exception) {
                openExternal(Uri.parse(url));
            }
        };
    }

    private void openIntent(Intent intent) {
        Uri uri = intent == null ? null : intent.getData();
        if (uri != null && isAllowedAppUri(uri)) {
            webView.loadUrl(addNativeParameter(uri).toString());
            return;
        }
        retryConnections();
    }

    private void retryConnections() {
        if (connectionAttemptInProgress) return;
        connectionAttemptInProgress = true;

        mainHandler.post(() -> {
            try {
                if (isFinishing() || webView == null) return;

                // Do not probe the network before navigation. Loading the hosted
                // origin directly gives WebView's persistent service worker/cache
                // the first chance to serve MauzoTrack while fully offline.
                webView.loadUrl(buildAppUrl(PRODUCTION_ORIGIN));
            } finally {
                connectionAttemptInProgress = false;
            }
        });
    }

    private List<String> candidateOrigins() {
        // Automatic local-network discovery is intentionally disabled in the
        // public app. Local/private addresses are manual developer overrides.
        List<String> origins = new ArrayList<>();
        origins.add(PRODUCTION_ORIGIN);
        return origins;
    }

    private String gatewayOrigin() {
        try {
            WifiManager manager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
            DhcpInfo info = manager == null ? null : manager.getDhcpInfo();
            if (info == null || info.gateway == 0) return null;
            String address = Formatter.formatIpAddress(info.gateway);
            return normalizeOrigin("http://" + address + ":8000");
        } catch (Exception ignored) {
            return null;
        }
    }

    private boolean probeOrigin(String origin) {
        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection) new URL(buildAppUrl(origin)).openConnection();
            connection.setConnectTimeout(CONNECT_TIMEOUT_MS);
            connection.setReadTimeout(CONNECT_TIMEOUT_MS);
            connection.setInstanceFollowRedirects(true);
            connection.setRequestProperty("User-Agent", "MauzoTrackNative/Android/" + VERSION);
            connection.setRequestProperty("Cache-Control", "no-cache");
            int status = connection.getResponseCode();
            return status >= 200 && status < 500;
        } catch (Exception ignored) {
            return false;
        } finally {
            if (connection != null) connection.disconnect();
        }
    }

    private String buildAppUrl(String origin) {
        return Uri.parse(origin).buildUpon()
            .path("/pos")
            .clearQuery()
            .appendQueryParameter("native_app", "android")
            .build()
            .toString();
    }

    private Uri addNativeParameter(Uri uri) {
        return uri.buildUpon().appendQueryParameter("native_app", "android").build();
    }

    private String normalizeOrigin(String value) {
        if (value == null || value.trim().isEmpty()) return null;
        String candidate = value.trim();
        if (!candidate.matches("(?i)^https?://.*")) candidate = "http://" + candidate;
        try {
            Uri uri = Uri.parse(candidate);
            if (!isAllowedAppUri(uri)) return null;
            int port = uri.getPort();
            return uri.getScheme() + "://" + uri.getHost() + (port > 0 ? ":" + port : "");
        } catch (Exception ignored) {
            return null;
        }
    }

    private boolean isAllowedAppUri(Uri uri) {
        if (uri == null || uri.getHost() == null) return false;
        String scheme = uri.getScheme() == null ? "" : uri.getScheme().toLowerCase();
        String host = uri.getHost().toLowerCase();
        if ("https".equals(scheme) && "app.sheanpackaging.com".equals(host)) return true;
        return "http".equals(scheme) && isPrivateHost(host);
    }

    private boolean isPrivateHost(String host) {
        if ("localhost".equals(host) || "127.0.0.1".equals(host) || "::1".equals(host)) return true;
        String[] parts = host.split("\\.");
        if (parts.length != 4) return false;
        try {
            int first = Integer.parseInt(parts[0]);
            int second = Integer.parseInt(parts[1]);
            return first == 10 || (first == 172 && second >= 16 && second <= 31) || (first == 192 && second == 168);
        } catch (NumberFormatException ignored) {
            return false;
        }
    }

    private void saveOrigin(String origin) {
        getSharedPreferences(PREFERENCES, MODE_PRIVATE).edit().putString(SERVER_KEY, origin).apply();
    }

    private void showOffline() {
        mainHandler.post(() -> {
            if (webView != null && !isFinishing()) webView.loadUrl("file:///android_asset/offline.html");
        });
    }

    private void openExternal(Uri uri) {
        try {
            startActivity(new Intent(Intent.ACTION_VIEW, uri));
        } catch (ActivityNotFoundException ignored) {
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        openIntent(intent);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == FILE_CHOOSER_REQUEST && fileChooserCallback != null) {
            fileChooserCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
            fileChooserCallback = null;
        }
    }

    @Override
    public void onBackPressed() {
        if (webView != null && webView.canGoBack()) webView.goBack(); else super.onBackPressed();
    }

    @Override
    protected void onDestroy() {
        connectionExecutor.shutdownNow();
        if (webView != null) {
            webView.removeJavascriptInterface("MauzoTrackNative");
            webView.destroy();
            webView = null;
        }
        super.onDestroy();
    }

    public class NativeBridge {
        @JavascriptInterface public String getPlatform() { return "android"; }
        @JavascriptInterface public String getVersion() { return VERSION; }
        @JavascriptInterface public boolean isNative() { return true; }
        @JavascriptInterface public void retryConnection() { mainHandler.post(MainActivity.this::retryConnections); }
        @JavascriptInterface public void connectToServer(String value) {
            String origin = normalizeOrigin(value);
            if (origin == null) {
                mainHandler.post(() -> webView.evaluateJavascript("window.showConnectionError(" + JSONObject.quote("Enter a private local address such as 192.168.137.1:8000.") + ")", null));
                return;
            }
            // Manual developer override applies only to this explicit action.
            // It never becomes the automatic startup workspace.
            mainHandler.post(() -> {
                if (webView != null && !isFinishing()) {
                    webView.loadUrl(buildAppUrl(origin));
                }
            });
        }
        @JavascriptInterface public void openAppSettings() {
            mainHandler.post(() -> startActivity(new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName()))));
        }
    }
}
