package com.zhongqu.vrviewer.demojava;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ScrollView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.zhongqu.vrviewer.ZQVRViewerManager;
import com.zhongqu.vrviewer.ZQViewerStateListener;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * 对齐 Swift {@code ZQVRViewer_Demo_Swift/ViewController.swift}：单框完整带看 URL、仅持久化 baseURL、
 * 先 openViewer 再 loadUrl、全屏 Web 层、Toolbar「音量/日志」浮层、结束带看销毁 WebView。
 */
abstract class BaseViewerActivity extends AppCompatActivity implements ZQViewerStateListener {

    private static final String TAG = "ViewerDemoJava";
    private static final long OVERLAY_ANIM_MS = 220L;
    private static final int REQ_RECORD_AUDIO = 0x3a1;
    private static final int REQ_CAMERA_FOR_SCAN = 0x3a2;
    private static final int REQUEST_CODE_SCAN = 0x3a3;

    @Nullable
    private Uri pendingEnterTourAfterMicPermission;

    private static final String PREFS_NAME = "ZQVRViewerDemo.TourConfig";
    private static final String DEFAULT_TOUR_PAGE_URL =
            "http://123.56.161.139:8080/play2/?m=b5fabc8e_Xmz4_b6f9&useVrTakeSee=1&userId=custom_01&callerUserId=custom_01&packageId=1001&imPlatform=app&takeSeeTestMode=1&house_id=1&vconsole=1&takeSeeTestMode=1";
    private static final String LEGACY_PLAY2_BASE = "http://123.56.161.139:8080/play2/";

    private final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss.SSS", Locale.US);

    @Nullable
    private View hostedWebView;

    private Toolbar toolbar;
    private EditText editBaseUrl;
    private Button btnScanTourUrl;
    private Button btnEnterTour;
    private FrameLayout layerTour;
    private FrameLayout webContainer;
    private Button btnEndTour;

    private View volumeBackdrop;
    private View volumePanel;
    private View logBackdrop;
    private View logPanel;

    private TextView labelCapture;
    private TextView labelPlayout;
    private SeekBar seekCapture;
    private SeekBar seekPlayout;
    private TextView logText;
    private ScrollView logScroll;

    private boolean volumeOverlayVisible;
    private boolean logOverlayVisible;

    @Nullable
    private MenuItem volumeMenuItem;
    @Nullable
    private MenuItem logMenuItem;

    protected abstract View createViewerWebView(Context context);

    protected abstract void configureViewerWebView(View webView);

    protected abstract void openViewerSdk(View webView);

    protected abstract void loadUrlOnViewer(View webView, String url);

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ZQVRViewerManager.registerViewerStateListener(this);

        toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        ActionBar ab = getSupportActionBar();
        if (ab != null) {
            ab.setTitle(R.string.demo_screen_title);
        }

        editBaseUrl = findViewById(R.id.edit_base_url);
        btnScanTourUrl = findViewById(R.id.btn_scan_tour_url);
        btnEnterTour = findViewById(R.id.btn_enter_tour);
        layerTour = findViewById(R.id.layer_tour);
        webContainer = findViewById(R.id.webview_container);
        btnEndTour = findViewById(R.id.btn_end_tour);

        volumeBackdrop = findViewById(R.id.volume_backdrop);
        volumePanel = findViewById(R.id.volume_panel);
        logBackdrop = findViewById(R.id.log_backdrop);
        logPanel = findViewById(R.id.log_panel);

        labelCapture = findViewById(R.id.label_capture_vol);
        labelPlayout = findViewById(R.id.label_playout_vol);
        seekCapture = findViewById(R.id.seek_capture_vol);
        seekPlayout = findViewById(R.id.seek_playout_vol);
        logText = findViewById(R.id.log_text);
        logScroll = findViewById(R.id.log_scroll);

        TextView kernelBanner = findViewById(R.id.text_kernel_banner);
        String kernelName =
                "WEBVIEW".equals(BuildConfig.BUILT_IN_KERNEL)
                        ? getString(R.string.kernel_label_webview)
                        : getString(R.string.kernel_label_x5);
        kernelBanner.setText(getString(R.string.kernel_banner_fmt, kernelName));
        onKernelBannerReady(kernelBanner);

        migrateLegacyMIfNeeded();
        applySavedTourConfiguration();

        btnEnterTour.setOnClickListener(v -> onEnterTourTapped());
        btnEndTour.setOnClickListener(v -> onEndTourTapped());
        btnScanTourUrl.setOnClickListener(v -> onScanTourUrlTapped());

        volumeBackdrop.setOnClickListener(v -> hideVolumeOverlay(true));
        logBackdrop.setOnClickListener(v -> hideLogOverlay(true));

        seekCapture.setOnSeekBarChangeListener(
                new SeekBar.OnSeekBarChangeListener() {
                    @Override
                    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                        labelCapture.setText(getString(R.string.capture_vol_fmt, progress));
                        if (fromUser) {
                            ZQVRViewerManager.setAudioCaptureVolume(progress);
                        }
                    }

                    @Override
                    public void onStartTrackingTouch(SeekBar seekBar) {}

                    @Override
                    public void onStopTrackingTouch(SeekBar seekBar) {}
                });

        seekPlayout.setOnSeekBarChangeListener(
                new SeekBar.OnSeekBarChangeListener() {
                    @Override
                    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                        labelPlayout.setText(getString(R.string.playout_vol_fmt, progress));
                        if (fromUser) {
                            ZQVRViewerManager.setAudioPlayoutVolume(progress);
                        }
                    }

                    @Override
                    public void onStartTrackingTouch(SeekBar seekBar) {}

                    @Override
                    public void onStopTrackingTouch(SeekBar seekBar) {}
                });

        labelCapture.setText(getString(R.string.capture_vol_fmt, seekCapture.getProgress()));
        labelPlayout.setText(getString(R.string.playout_vol_fmt, seekPlayout.getProgress()));

        appendLog(getString(R.string.log_ready));
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.demo_main, menu);
        volumeMenuItem = menu.findItem(R.id.action_volume);
        logMenuItem = menu.findItem(R.id.action_log);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_volume) {
            onVolumeMenuTapped();
            return true;
        }
        if (id == R.id.action_log) {
            onLogMenuTapped();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private SharedPreferences tourPrefs() {
        return getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
    }

    private void migrateLegacyMIfNeeded() {
        SharedPreferences p = tourPrefs();
        String legacyM = trimToEmpty(p.getString("m", ""));
        if (legacyM.isEmpty()) {
            return;
        }
        String base = trimToEmpty(p.getString("baseURL", ""));
        if (base.isEmpty()) {
            base = LEGACY_PLAY2_BASE;
        }
        Uri u = Uri.parse(base);
        if (u.getScheme() == null || u.getScheme().isEmpty()) {
            p.edit().remove("m").apply();
            return;
        }
        if (u.getQueryParameterNames().contains("m")) {
            p.edit().remove("m").apply();
            return;
        }
        String merged = u.buildUpon().appendQueryParameter("m", legacyM).build().toString();
        p.edit().putString("baseURL", merged).remove("m").apply();
    }

    private void applySavedTourConfiguration() {
        SharedPreferences p = tourPrefs();

        String base = trimToEmpty(p.getString("baseURL", ""));
        if (!base.isEmpty()) {
            editBaseUrl.setText(base);
        } else {
            editBaseUrl.setText(DEFAULT_TOUR_PAGE_URL);
        }
    }

    private void persistTourConfiguration() {
        tourPrefs().edit().putString("baseURL", normalizedBaseUrlInput()).apply();
    }

    private static String trimToEmpty(@Nullable String s) {
        return s == null ? "" : s.trim();
    }

    private String normalizedBaseUrlInput() {
        CharSequence cs = editBaseUrl.getText();
        if (cs == null) {
            return "";
        }
        return cs.toString().replace("\n", "").replace("\r", "").trim();
    }

    @Nullable
    private Uri buildTourUri() {
        String raw = normalizedBaseUrlInput();
        if (raw.isEmpty()) {
            return null;
        }
        Uri u = Uri.parse(raw);
        String scheme = u.getScheme();
        if (scheme == null || scheme.isEmpty()) {
            u = Uri.parse("https://" + raw.trim());
            scheme = u.getScheme();
        }
        if (scheme == null || scheme.isEmpty()) {
            return null;
        }
        return u;
    }

    private void applyTourRoleTitle(@NonNull Uri tourUrl) {
        String uid = trimToEmpty(tourUrl.getQueryParameter("userId"));
        String caller = trimToEmpty(tourUrl.getQueryParameter("callerUserId"));
        ActionBar ab = getSupportActionBar();
        if (ab == null) {
            return;
        }
        if (uid.isEmpty() && caller.isEmpty()) {
            ab.setTitle(R.string.title_touring);
            return;
        }
        ab.setTitle(uid.equals(caller) ? R.string.title_caller : R.string.title_callee);
    }

    private void onScanTourUrlTapped() {
        appendLog(getString(R.string.log_scan_opening));
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
            appendLog(getString(R.string.log_request_camera_permission));
            ActivityCompat.requestPermissions(
                    this, new String[] {Manifest.permission.CAMERA}, REQ_CAMERA_FOR_SCAN);
            return;
        }
        startActivityForResult(QrScanActivity.createIntent(this), REQUEST_CODE_SCAN);
    }

    private void hideIme() {
        View focus = getCurrentFocus();
        if (focus == null) {
            return;
        }
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null) {
            imm.hideSoftInputFromWindow(focus.getWindowToken(), 0);
        }
    }

    private void onEnterTourTapped() {
        hideIme();
        if (!isViewerWebViewAllowed()) {
            appendLog(getString(R.string.log_x5_kernel_not_ready));
            return;
        }
        if (hostedWebView != null) {
            appendLog(getString(R.string.log_already_in_tour));
            return;
        }
        Uri url = buildTourUri();
        if (url == null) {
            appendLog(getString(R.string.log_invalid_tour_url));
            return;
        }
        persistTourConfiguration();
        appendLog(getString(R.string.log_config_saved));
        applyTourRoleTitle(url);

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
                != PackageManager.PERMISSION_GRANTED) {
            pendingEnterTourAfterMicPermission = url;
            appendLog(getString(R.string.log_request_mic_permission));
            ActivityCompat.requestPermissions(
                    this, new String[] {Manifest.permission.RECORD_AUDIO}, REQ_RECORD_AUDIO);
            return;
        }
        proceedEnterTour(url);
    }

    private void proceedEnterTour(Uri url) {
        View wv = createViewerWebView(this);
        webContainer.addView(
                wv,
                new FrameLayout.LayoutParams(
                        FrameLayout.LayoutParams.MATCH_PARENT,
                        FrameLayout.LayoutParams.MATCH_PARENT));
        configureViewerWebView(wv);
        openViewerSdk(wv);
        hostedWebView = wv;
        appendLog(getString(R.string.log_open_and_load));

        loadUrlOnViewer(wv, url.toString());
        onAfterViewerUrlLoaded(wv, url.toString());
        appendLog(getString(R.string.log_loaded_remote, url.toString()));

        layerTour.setVisibility(View.VISIBLE);
    }

    private void onEndTourTapped() {
        hideVolumeOverlay(false);
        hideLogOverlay(false);
        ZQVRViewerManager.destroyViewer();
        webContainer.removeAllViews();
        hostedWebView = null;
        layerTour.setVisibility(View.GONE);
        ActionBar ab = getSupportActionBar();
        if (ab != null) {
            ab.setTitle(R.string.demo_screen_title);
        }
        appendLog(getString(R.string.log_destroy_done));
    }

    private void onVolumeMenuTapped() {
        if (volumeOverlayVisible) {
            hideVolumeOverlay(true);
            return;
        }
        hideLogOverlay(false);
        volumeOverlayVisible = true;
        volumeBackdrop.setVisibility(View.VISIBLE);
        volumePanel.setVisibility(View.VISIBLE);
        volumeBackdrop.animate().cancel();
        volumePanel.animate().cancel();
        volumeBackdrop.setAlpha(0f);
        volumePanel.setAlpha(0f);
        volumeBackdrop.animate().alpha(1f).setDuration(OVERLAY_ANIM_MS).start();
        volumePanel.animate().alpha(1f).setDuration(OVERLAY_ANIM_MS).start();
        if (volumeMenuItem != null) {
            volumeMenuItem.setTitle(R.string.menu_collapse);
        }
    }

    private void hideVolumeOverlay(boolean animated) {
        if (!volumeOverlayVisible) {
            if (volumeMenuItem != null) {
                volumeMenuItem.setTitle(R.string.menu_volume);
            }
            return;
        }
        volumeOverlayVisible = false;
        if (volumeMenuItem != null) {
            volumeMenuItem.setTitle(R.string.menu_volume);
        }
        Runnable finish =
                () -> {
                    volumeBackdrop.setVisibility(View.GONE);
                    volumePanel.setVisibility(View.GONE);
                };
        if (animated) {
            volumeBackdrop.animate().alpha(0f).setDuration(OVERLAY_ANIM_MS).withEndAction(finish).start();
            volumePanel.animate().alpha(0f).setDuration(OVERLAY_ANIM_MS).start();
        } else {
            volumeBackdrop.animate().cancel();
            volumePanel.animate().cancel();
            volumeBackdrop.setAlpha(0f);
            volumePanel.setAlpha(0f);
            finish.run();
        }
    }

    private void onLogMenuTapped() {
        if (logOverlayVisible) {
            hideLogOverlay(true);
            return;
        }
        hideVolumeOverlay(false);
        logOverlayVisible = true;
        logBackdrop.setVisibility(View.VISIBLE);
        logPanel.setVisibility(View.VISIBLE);
        logBackdrop.animate().cancel();
        logPanel.animate().cancel();
        logBackdrop.setAlpha(0f);
        logPanel.setAlpha(0f);
        logBackdrop.animate().alpha(1f).setDuration(OVERLAY_ANIM_MS).start();
        logPanel.animate().alpha(1f).setDuration(OVERLAY_ANIM_MS).start();
        if (logMenuItem != null) {
            logMenuItem.setTitle(R.string.menu_collapse);
        }
    }

    private void hideLogOverlay(boolean animated) {
        if (!logOverlayVisible) {
            if (logMenuItem != null) {
                logMenuItem.setTitle(R.string.menu_log);
            }
            return;
        }
        logOverlayVisible = false;
        if (logMenuItem != null) {
            logMenuItem.setTitle(R.string.menu_log);
        }
        Runnable finish =
                () -> {
                    logBackdrop.setVisibility(View.GONE);
                    logPanel.setVisibility(View.GONE);
                };
        if (animated) {
            logBackdrop.animate().alpha(0f).setDuration(OVERLAY_ANIM_MS).withEndAction(finish).start();
            logPanel.animate().alpha(0f).setDuration(OVERLAY_ANIM_MS).start();
        } else {
            logBackdrop.animate().cancel();
            logPanel.animate().cancel();
            logBackdrop.setAlpha(0f);
            logPanel.setAlpha(0f);
            finish.run();
        }
    }

    /**
     * 子类（如 x5Kernel）可调用，将一行诊断输出到 Demo 日志面板。
     */
    protected final void appendViewerDemoLog(@NonNull String message) {
        appendLog(message);
    }

    /**
     * 在「内核横幅」文案就绪后调用；x5Kernel 可覆写以挂载 X5 探测等。
     */
    protected void onKernelBannerReady(@NonNull TextView kernelBanner) {}

    /** x5Kernel 可在 TBS preInit / onCoreInitFinished 完成前返回 false，阻止创建 smtt WebView。 */
    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    protected boolean isViewerWebViewAllowed() {
        return true;
    }

    /**
     * 在 {@link #loadUrlOnViewer(View, String)} 之后调用；x5Kernel 可覆写以在 loadUrl 后做与 publicDemo
     * WebActivity 一致的内核验证。
     */
    protected void onAfterViewerUrlLoaded(@NonNull View webView, @NonNull String url) {}

    private void appendLog(String message) {
        String line = "[" + timeFormat.format(new Date()) + "] " + message + "\n";
        Log.i(TAG, line.trim());
        logText.append(line);
        logScroll.post(() -> logScroll.fullScroll(View.FOCUS_DOWN));
    }

    @Override
    public void onRequestPermissionsResult(
            int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQ_CAMERA_FOR_SCAN) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                startActivityForResult(QrScanActivity.createIntent(this), REQUEST_CODE_SCAN);
            } else {
                appendLog(getString(R.string.log_camera_permission_denied));
                Toast.makeText(this, R.string.toast_scan_camera_denied, Toast.LENGTH_LONG).show();
            }
            return;
        }
        if (requestCode != REQ_RECORD_AUDIO) {
            return;
        }
        Uri pending = pendingEnterTourAfterMicPermission;
        pendingEnterTourAfterMicPermission = null;
        if (pending == null) {
            return;
        }
        if (grantResults.length == 0 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
            appendLog(getString(R.string.log_mic_permission_denied));
            return;
        }
        if (hostedWebView != null) {
            return;
        }
        if (!isViewerWebViewAllowed()) {
            appendLog(getString(R.string.log_x5_kernel_not_ready));
            return;
        }
        proceedEnterTour(pending);
    }

    @Override
    @SuppressWarnings("deprecation")
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode != REQUEST_CODE_SCAN) {
            return;
        }
        if (resultCode != Activity.RESULT_OK) {
            return;
        }
        if (data == null) {
            return;
        }
        String url = data.getStringExtra(QrScanActivity.EXTRA_TEXT);
        if (url == null) {
            return;
        }
        url = url.trim();
        if (url.isEmpty()) {
            return;
        }
        editBaseUrl.setText(url);
        appendLog(getString(R.string.log_scan_filled, url));
    }

    @Override
    protected void onDestroy() {
        ZQVRViewerManager.destroyViewer();
        super.onDestroy();
    }

    @Override
    public void onEnterRoom(int result) {
        appendLog("onEnterRoom: " + result);
        Log.i(TAG, "onEnterRoom " + result);
    }

    @Override
    public void onExitRoom(int reason) {
        appendLog("onExitRoom: reason=" + reason);
        Log.i(TAG, "onExitRoom " + reason);
    }

    @Override
    public void onError(int errCode, @Nullable String errMsg, @Nullable Bundle extraInfo) {
        appendLog("onError: code=" + errCode + " msg=" + errMsg);
        Log.w(TAG, "onError " + errCode + " " + errMsg);
    }

    @Override
    public void onRemoteUserEnterRoom(String userId) {
        appendLog("onRemoteUserEnterRoom: " + userId);
        Log.i(TAG, "onRemoteUserEnterRoom " + userId);
    }

    @Override
    public void onRemoteUserLeaveRoom(String userId, int reason) {
        appendLog("onRemoteUserLeaveRoom: " + userId + " reason=" + reason);
        Log.i(TAG, "onRemoteUserLeaveRoom " + userId + " " + reason);
    }

    @Override
    public void onFirstAudioFrame(String userId) {
        appendLog("onFirstAudioFrame: " + userId);
        Log.i(TAG, "onFirstAudioFrame " + userId);
    }

    @Override
    public void onNetworkQuality(String userId, int quality) {
        appendLog("onNetworkQuality: " + userId + " quality=" + quality);
        Log.i(TAG, "onNetworkQuality " + userId + " " + quality);
    }

    @Override
    public void onExitVR() {
        appendLog("onExitVR");
        Log.i(TAG, "onExitVR");
        onEndTourTapped();
    }
}
