/*
 ============================================================================
 Name        : MainActivity.java
 Author      : hev <r@hev.cc>
 Copyright   : Copyright (c) 2023 xyz
 Description : Main Activity
 ============================================================================
 */

package org.iiab.controller;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.app.AlertDialog;
import android.content.Intent;
import android.content.Context;
import android.content.IntentFilter;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.net.VpnService;
import android.net.Uri;
import android.text.method.ScrollingMovementMethod;
import android.os.Build;
import android.os.PowerManager;
import android.provider.Settings;

import androidx.annotation.NonNull;
import androidx.biometric.BiometricManager;
import androidx.biometric.BiometricPrompt;
import androidx.core.content.ContextCompat;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.concurrent.Executor;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
	private Preferences prefs;
	private EditText edittext_socks_addr;
	private EditText edittext_socks_udp_addr;
	private EditText edittext_socks_port;
	private EditText edittext_socks_user;
	private EditText edittext_socks_pass;
	private EditText edittext_dns_ipv4;
	private EditText edittext_dns_ipv6;
	private CheckBox checkbox_udp_in_tcp;
	private CheckBox checkbox_remote_dns;
	private CheckBox checkbox_global;
	private CheckBox checkbox_ipv4;
	private CheckBox checkbox_ipv6;
	private Button button_apps;
	private Button button_save;
	private Button button_control;
	
	private Button watchdogControl;
	private TextView connectionLog;
	private LinearLayout logActions;
	private Button btnClearLog;
	private Button btnCopyLog;
	private LinearLayout configLayout;
	private TextView configLabel;
	private LinearLayout advancedConfig;
	private TextView advConfigLabel;
	private TextView logLabel;
	private ImageButton themeToggle;
	private TextView versionFooter;

	private BroadcastReceiver logReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			if (IIABWatchdog.ACTION_LOG_MESSAGE.equals(intent.getAction())) {
				String message = intent.getStringExtra(IIABWatchdog.EXTRA_MESSAGE);
				addToLog(message);
			}
		}
	};

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		prefs = new Preferences(this);
		setContentView(R.layout.main);

		edittext_socks_addr = (EditText) findViewById(R.id.socks_addr);
		edittext_socks_udp_addr = (EditText) findViewById(R.id.socks_udp_addr);
		edittext_socks_port = (EditText) findViewById(R.id.socks_port);
		edittext_socks_user = (EditText) findViewById(R.id.socks_user);
		edittext_socks_pass = (EditText) findViewById(R.id.socks_pass);
		edittext_dns_ipv4 = (EditText) findViewById(R.id.dns_ipv4);
		edittext_dns_ipv6 = (EditText) findViewById(R.id.dns_ipv6);
		checkbox_ipv4 = (CheckBox) findViewById(R.id.ipv4);
		checkbox_ipv6 = (CheckBox) findViewById(R.id.ipv6);
		checkbox_global = (CheckBox) findViewById(R.id.global);
		checkbox_udp_in_tcp = (CheckBox) findViewById(R.id.udp_in_tcp);
		checkbox_remote_dns = (CheckBox) findViewById(R.id.remote_dns);
		button_apps = (Button) findViewById(R.id.apps);
		button_save = (Button) findViewById(R.id.save);
		button_control = (Button) findViewById(R.id.control);
		
		watchdogControl = (Button) findViewById(R.id.watchdog_control);
		watchdogControl.setOnClickListener(this);

		logActions = (LinearLayout) findViewById(R.id.log_actions);
		btnClearLog = (Button) findViewById(R.id.btn_clear_log);
		btnCopyLog = (Button) findViewById(R.id.btn_copy_log);
		btnClearLog.setOnClickListener(this);
		btnCopyLog.setOnClickListener(this);

		connectionLog = (TextView) findViewById(R.id.connection_log);
		connectionLog.setMovementMethod(new ScrollingMovementMethod());
		// Enable text selection for copying large logs
		connectionLog.setTextIsSelectable(true);
		
		configLayout = (LinearLayout) findViewById(R.id.config_layout);
		configLabel = (TextView) findViewById(R.id.config_label);
		configLabel.setOnClickListener(v -> toggleVisibility(configLayout, configLabel, "Configuration"));

		advancedConfig = (LinearLayout) findViewById(R.id.advanced_config);
		advConfigLabel = (TextView) findViewById(R.id.adv_config_label);
		advConfigLabel.setOnClickListener(v -> toggleVisibility(advancedConfig, advConfigLabel, "Advanced Settings"));

		logLabel = (TextView) findViewById(R.id.log_label);
		logLabel.setOnClickListener(v -> {
			if (connectionLog.getVisibility() == View.GONE) {
				readBlackBoxLogs(); // Load logs from file when expanding
			}
			toggleVisibility(connectionLog, logLabel, "Connection Log");
			logActions.setVisibility(connectionLog.getVisibility());
		});

		themeToggle = (ImageButton) findViewById(R.id.theme_toggle);
		themeToggle.setOnClickListener(v -> toggleTheme());
		applySavedTheme();

		versionFooter = (TextView) findViewById(R.id.version_text);
		setVersionFooter();

		checkbox_udp_in_tcp.setOnClickListener(this);
		checkbox_remote_dns.setOnClickListener(this);
		checkbox_global.setOnClickListener(this);
		button_apps.setOnClickListener(this);
		button_save.setOnClickListener(this);
		button_control.setOnClickListener(this);
		updateUI();

		/* Request VPN permission */
		Intent intent = VpnService.prepare(MainActivity.this);
		if (intent != null)
		  startActivityForResult(intent, 0);
		else
		  onActivityResult(0, RESULT_OK, null);
		
		checkBatteryOptimizations();
		addToLog("Application Started");
	}

	private void readBlackBoxLogs() {
		File logFile = new File(getFilesDir(), "watchdog_heartbeat_log.txt");
		if (!logFile.exists()) {
			addToLog("--- No BlackBox file found ---");
			return;
		}

		addToLog("--- Loading BlackBox Logs ---");
		try (BufferedReader br = new BufferedReader(new FileReader(logFile))) {
			String line;
			while ((line = br.readLine()) != null) {
				addToLog("[FILE] " + line);
			}
		} catch (IOException e) {
			addToLog("Error reading BlackBox: " + e.getMessage());
		}
		addToLog("--- End of File ---");
	}

	private void setVersionFooter() {
		try {
			PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
			String version = pInfo.versionName;
			versionFooter.setText(version);
		} catch (PackageManager.NameNotFoundException e) {
			versionFooter.setText("v0.1.x");
		}
	}

	@Override
	protected void onResume() {
		super.onResume();
		if (getIntent() != null && getIntent().getBooleanExtra(VpnRecoveryReceiver.EXTRA_RECOVERY, false)) {
			addToLog("Recovery Pulse Received from System. Enforcing VPN...");
			Intent vpnIntent = new Intent(this, TProxyService.class);
			startService(vpnIntent.setAction(TProxyService.ACTION_CONNECT));
			setIntent(null);
		}
	}

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

	private void checkBatteryOptimizations() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
            if (pm != null && !pm.isIgnoringBatteryOptimizations(getPackageName())) {
                new AlertDialog.Builder(this)
                    .setTitle("Battery Optimization")
                    .setMessage("For the Watchdog to work reliably, please disable battery optimizations for this app.")
                    .setPositiveButton("Go to Settings", (dialog, which) -> {
                        Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                        intent.setData(Uri.parse("package:" + getPackageName()));
                        startActivity(intent);
                    })
                    .setNegativeButton("Cancel", null)
                    .show();
            }
        }
    }

	private void toggleTheme() {
		SharedPreferences sharedPref = getPreferences(Context.MODE_PRIVATE);
		int currentMode = AppCompatDelegate.getDefaultNightMode();
		int nextMode;

		if (currentMode == AppCompatDelegate.MODE_NIGHT_NO) {
			nextMode = AppCompatDelegate.MODE_NIGHT_YES;
		} else if (currentMode == AppCompatDelegate.MODE_NIGHT_YES) {
			nextMode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
		} else {
			nextMode = AppCompatDelegate.MODE_NIGHT_NO;
		}

		SharedPreferences.Editor editor = sharedPref.edit();
		editor.putInt("ui_mode", nextMode);
		editor.apply();

		AppCompatDelegate.setDefaultNightMode(nextMode);
		updateThemeToggleButton(nextMode);
	}

	private void applySavedTheme() {
		SharedPreferences sharedPref = getPreferences(Context.MODE_PRIVATE);
		int savedMode = sharedPref.getInt("ui_mode", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
		AppCompatDelegate.setDefaultNightMode(savedMode);
		updateThemeToggleButton(savedMode);
	}

	private void updateThemeToggleButton(int mode) {
		if (mode == AppCompatDelegate.MODE_NIGHT_NO) {
			themeToggle.setImageResource(R.drawable.ic_theme_dark);
		} else if (mode == AppCompatDelegate.MODE_NIGHT_YES) {
			themeToggle.setImageResource(R.drawable.ic_theme_light);
		} else {
			themeToggle.setImageResource(R.drawable.ic_theme_system);
		}
	}

	private void toggleVisibility(View view, TextView label, String text) {
		if (view.getVisibility() == View.GONE) {
			view.setVisibility(View.VISIBLE);
			label.setText("▼ " + text);
		} else {
			view.setVisibility(View.GONE);
			label.setText("▶ " + text);
		}
	}

	@Override
	protected void onStart() {
		super.onStart();
		IntentFilter filter = new IntentFilter(IIABWatchdog.ACTION_LOG_MESSAGE);
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
			registerReceiver(logReceiver, filter, Context.RECEIVER_EXPORTED);
		} else {
			registerReceiver(logReceiver, filter);
		}
	}

	@Override
	protected void onStop() {
		super.onStop();
		try {
			unregisterReceiver(logReceiver);
		} catch (Exception e) {
			// Ignore
		}
	}

	@Override
	protected void onActivityResult(int request, int result, Intent data) {
		super.onActivityResult(request, result, data);
		if ((result == RESULT_OK) && prefs.getEnable()) {
			Intent intent = new Intent(this, TProxyService.class);
			startService(intent.setAction(TProxyService.ACTION_CONNECT));
			addToLog("VPN Permission Granted. Connecting...");
		}
	}

	@Override
	public void onClick(View view) {
		if (view == checkbox_global || view == checkbox_remote_dns) {
			savePrefs();
			updateUI();
		} else if (view == button_apps) {
			startActivity(new Intent(this, AppListActivity.class));
		} else if (view == button_save) {
			savePrefs();
			Context context = getApplicationContext();
			Toast.makeText(context, "Saved", Toast.LENGTH_SHORT).show();
			addToLog("Settings Saved");
		} else if (view.getId() == R.id.control) {
			handleControlClick();
		} else if (view.getId() == R.id.watchdog_control) {
			handleWatchdogClick();
		} else if (view.getId() == R.id.btn_clear_log) {
			connectionLog.setText("");
			addToLog("Log reset");
		} else if (view.getId() == R.id.btn_copy_log) {
			ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
			ClipData clip = ClipData.newPlainText("IIAB Log", connectionLog.getText().toString());
			if (clipboard != null) {
				clipboard.setPrimaryClip(clip);
				Toast.makeText(this, "Log copied to clipboard", Toast.LENGTH_SHORT).show();
			}
		}
	}

	private void handleWatchdogClick() {
		boolean isEnabled = prefs.getWatchdogEnable();
		if (isEnabled) {
			showWatchdogBiometricPrompt();
		} else {
			toggleWatchdog(false);
		}
	}

	private void toggleWatchdog(boolean stop) {
		prefs.setWatchdogEnable(!stop);
		Intent intent = new Intent(this, WatchdogService.class);
		if (stop) {
			stopService(intent);
			addToLog("Watchdog Stopping...");
		} else {
			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
				startForegroundService(intent.setAction(WatchdogService.ACTION_START));
			} else {
				startService(intent.setAction(WatchdogService.ACTION_START));
			}
			addToLog("Watchdog Starting...");
		}
		updateUI();
	}

	private void handleControlClick() {
		boolean isCurrentlyEnabled = prefs.getEnable();
		if (isCurrentlyEnabled) {
			showBiometricPrompt();
		} else {
			BiometricManager biometricManager = BiometricManager.from(this);
			int authenticators = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) ? 
				(BiometricManager.Authenticators.BIOMETRIC_STRONG | BiometricManager.Authenticators.DEVICE_CREDENTIAL) : 
				BiometricManager.Authenticators.BIOMETRIC_WEAK;

			boolean isSecure = false;
			android.app.KeyguardManager km = (android.app.KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
			if (km != null && km.isDeviceSecure()) isSecure = true;

			if (biometricManager.canAuthenticate(authenticators) == BiometricManager.BIOMETRIC_SUCCESS || isSecure) {
				addToLog("User initiated connection");
				toggleService(false);
			} else {
				showEnrollmentDialog();
			}
		}
	}

	private void showEnrollmentDialog() {
		new AlertDialog.Builder(this)
			.setTitle("Security Required")
			.setMessage("You must set up a PIN, Pattern, or Fingerprint on your device before enabling the secure environment.")
			.setPositiveButton("Go to Settings", (dialog, which) -> {
				Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
				startActivity(intent);
			})
			.setNegativeButton("Cancel", null)
			.show();
	}

	private void showBiometricPrompt() {
		Executor executor = ContextCompat.getMainExecutor(this);
		BiometricPrompt biometricPrompt = new BiometricPrompt(MainActivity.this,
				executor, new BiometricPrompt.AuthenticationCallback() {
			@Override
			public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
				super.onAuthenticationSucceeded(result);
				addToLog("Authentication Success. Disconnecting...");
				toggleService(true);
			}
		});

		BiometricPrompt.PromptInfo.Builder promptBuilder = new BiometricPrompt.PromptInfo.Builder()
				.setTitle("Authentication required")
				.setSubtitle("Authenticate to disable the secure environment");

		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
			promptBuilder.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG | 
					BiometricManager.Authenticators.DEVICE_CREDENTIAL);
		} else {
			promptBuilder.setDeviceCredentialAllowed(true);
		}
		biometricPrompt.authenticate(promptBuilder.build());
	}

	private void showWatchdogBiometricPrompt() {
		Executor executor = ContextCompat.getMainExecutor(this);
		BiometricPrompt biometricPrompt = new BiometricPrompt(this, executor, new BiometricPrompt.AuthenticationCallback() {
			@Override
			public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
				super.onAuthenticationSucceeded(result);
				toggleWatchdog(true);
			}
		});

		BiometricPrompt.PromptInfo.Builder promptBuilder = new BiometricPrompt.PromptInfo.Builder()
				.setTitle("Unlock Master Watchdog")
				.setSubtitle("Authentication required to stop Termux protection");

		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
			promptBuilder.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG | 
					BiometricManager.Authenticators.DEVICE_CREDENTIAL);
		} else {
			promptBuilder.setDeviceCredentialAllowed(true);
		}
		biometricPrompt.authenticate(promptBuilder.build());
	}

	private void toggleService(boolean isEnable) {
		prefs.setEnable(!isEnable);
		savePrefs();
		updateUI();
		Intent intent = new Intent(this, TProxyService.class);
		if (isEnable) {
			startService(intent.setAction(TProxyService.ACTION_DISCONNECT));
			addToLog("VPN Stopping...");
		} else {
			startService(intent.setAction(TProxyService.ACTION_CONNECT));
			addToLog("VPN Starting...");
		}
	}

	private void updateUI() {
		boolean vpnActive = prefs.getEnable();
		boolean watchdogActive = prefs.getWatchdogEnable();

		if (vpnActive) {
			button_control.setText(R.string.control_disable);
			button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_on));
		} else {
			button_control.setText(R.string.control_enable);
			button_control.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_vpn_off));
		}

		if (watchdogActive) {
			watchdogControl.setText("Disable Master Watchdog");
			watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_on));
		} else {
			watchdogControl.setText("Enable Master Watchdog");
			watchdogControl.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.btn_watchdog_off));
		}
		
		edittext_socks_addr.setText(prefs.getSocksAddress());
		edittext_socks_udp_addr.setText(prefs.getSocksUdpAddress());
		edittext_socks_port.setText(String.valueOf(prefs.getSocksPort()));
		edittext_socks_user.setText(prefs.getSocksUsername());
		edittext_socks_pass.setText(prefs.getSocksPassword());
		edittext_dns_ipv4.setText(prefs.getDnsIpv4());
		edittext_dns_ipv6.setText(prefs.getDnsIpv6());
		checkbox_ipv4.setChecked(prefs.getIpv4());
		checkbox_ipv6.setChecked(prefs.getIpv6());
		checkbox_global.setChecked(prefs.getGlobal());
		checkbox_udp_in_tcp.setChecked(prefs.getUdpInTcp());
		checkbox_remote_dns.setChecked(prefs.getRemoteDns());

		boolean editable = !vpnActive;
		edittext_socks_addr.setEnabled(editable);
		edittext_socks_port.setEnabled(editable);
		button_save.setEnabled(editable);
	}

	private void savePrefs() {
		prefs.setSocksAddress(edittext_socks_addr.getText().toString());
		prefs.setSocksPort(Integer.parseInt(edittext_socks_port.getText().toString()));
		prefs.setSocksUdpAddress(edittext_socks_udp_addr.getText().toString());
		prefs.setSocksUsername(edittext_socks_user.getText().toString());
		prefs.setSocksPassword(edittext_socks_pass.getText().toString());
		prefs.setDnsIpv4(edittext_dns_ipv4.getText().toString());
		prefs.setDnsIpv6(edittext_dns_ipv6.getText().toString());
		prefs.setIpv4(checkbox_ipv4.isChecked());
		prefs.setIpv6(checkbox_ipv6.isChecked());
		prefs.setGlobal(checkbox_global.isChecked());
		prefs.setUdpInTcp(checkbox_udp_in_tcp.isChecked());
		prefs.setRemoteDns(checkbox_remote_dns.isChecked());
	}
	
	private void addToLog(String message) {
		runOnUiThread(() -> {
			SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
			String currentTime = sdf.format(new Date());
			String logEntry = "[" + currentTime + "] " + message + "\n";
			if (connectionLog != null) {
				connectionLog.append(logEntry);
				// Automatic scrolling to bottom
				final int scrollAmount = connectionLog.getLayout() != null ? 
					connectionLog.getLayout().getLineTop(connectionLog.getLineCount()) - connectionLog.getHeight() : 0;
				if (scrollAmount > 0)
					connectionLog.scrollTo(0, scrollAmount);
			}
		});
	}
}
