package com.dragonssh.xhttpdemo;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.VpnService;
import android.os.Build;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.dragonssh.xhttpdemo.core.config.Settings;
import com.dragonssh.xhttpdemo.core.logger.ConnectionStatus;
import com.dragonssh.xhttpdemo.core.logger.SkStatus;
import com.dragonssh.xhttpdemo.core.tunnel.TunnelManagerHelper;
import com.google.android.material.button.MaterialButton;

public final class MainActivity extends AppCompatActivity implements SkStatus.StateListener {
    private static final int NOTIFICATION_PERMISSION_REQUEST = 42;

    private Settings settings;
    private TextView statusView;
    private TextView serverValue;
    private TextView usernameValue;
    private TextView sniValue;
    private TextView hostValue;
    private TextView pathValue;
    private TextView tlsModeValue;
    private TextView xhttpTlsValue;
    private MaterialButton startStopButton;
    private boolean tunnelActive;

    private final ActivityResultLauncher<Intent> settingsLauncher =
            registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> refreshSummary());

    private final ActivityResultLauncher<Intent> vpnPermissionLauncher =
            registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
                if (result.getResultCode() == RESULT_OK) {
                    startTunnel();
                } else {
                    Toast.makeText(this, R.string.vpn_permission_required, Toast.LENGTH_LONG).show();
                }
            });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        WindowInsetsHelper.apply(this, R.id.main_root);

        settings = new Settings(this);
        bindViews();

        startStopButton.setOnClickListener(v -> {
            if (tunnelActive || SkStatus.isTunnelActive()) {
                TunnelManagerHelper.stopXHttpSsh(this);
            } else {
                requestVpnAndConnect();
            }
        });
        findViewById(R.id.open_settings).setOnClickListener(v -> openSettings());
        findViewById(R.id.open_vpn_settings).setOnClickListener(v ->
                settingsLauncher.launch(new Intent(this, VpnSettingsActivity.class)));
        findViewById(R.id.open_logs).setOnClickListener(v ->
                startActivity(new Intent(this, LogsActivity.class)));

        refreshSummary();
        requestNotificationPermissionIfNeeded();
    }

    @Override
    protected void onStart() {
        super.onStart();
        SkStatus.addStateListener(this);
        refreshSummary();
    }

    @Override
    protected void onStop() {
        SkStatus.removeStateListener(this);
        super.onStop();
    }

    private void bindViews() {
        statusView = findViewById(R.id.status);
        serverValue = findViewById(R.id.summary_server);
        usernameValue = findViewById(R.id.summary_username);
        sniValue = findViewById(R.id.summary_sni);
        hostValue = findViewById(R.id.summary_xhttp_host);
        pathValue = findViewById(R.id.summary_xhttp_path);
        tlsModeValue = findViewById(R.id.summary_tls_mode);
        xhttpTlsValue = findViewById(R.id.summary_xhttp_tls);
        startStopButton = findViewById(R.id.start_stop);
    }

    private void refreshSummary() {
        if (settings == null) return;
        String server = settings.getPrivString(Settings.XHTTP_ENDPOINT_KEY);
        String port = settings.getPrivString(Settings.XHTTP_PORT_KEY);
        String username = settings.getPrivString(Settings.USUARIO_KEY);
        String sni = settings.getPrivString(Settings.CUSTOM_SNI);
        String host = settings.getPrivString(Settings.XHTTP_HOST_KEY);
        String path = ConfigUtils.normalizePath(settings.getPrivString(Settings.XHTTP_PATH_KEY));
        boolean tls = !"0".equals(settings.getPrivString(Settings.XHTTP_TLS_KEY));

        serverValue.setText(ConfigUtils.formatHostPort(server, port));
        usernameValue.setText(ConfigUtils.displayValue(username));
        sniValue.setText(ConfigUtils.displayValue(sni));
        hostValue.setText(ConfigUtils.displayValue(host));
        pathValue.setText(ConfigUtils.displayValue(path));
        tlsModeValue.setText(tls ? R.string.tls_mode_automatic : R.string.tls_mode_disabled);
        xhttpTlsValue.setText(tls ? R.string.enabled : R.string.disabled);
    }

    private void openSettings() {
        settingsLauncher.launch(new Intent(this, SettingsActivity.class));
    }

    private void requestVpnAndConnect() {
        String error = validateSavedConfiguration();
        if (error != null) {
            Toast.makeText(this, error, Toast.LENGTH_LONG).show();
            openSettings();
            return;
        }

        DemoApplication application = (DemoApplication) getApplication();
        if (!application.hasSessionPassword()) {
            Toast.makeText(this, R.string.password_needed_for_session, Toast.LENGTH_LONG).show();
            openSettings();
            return;
        }

        Intent permissionIntent = VpnService.prepare(this);
        if (permissionIntent != null) {
            vpnPermissionLauncher.launch(permissionIntent);
        } else {
            startTunnel();
        }
    }

    private String validateSavedConfiguration() {
        String server = settings.getPrivString(Settings.XHTTP_ENDPOINT_KEY).trim();
        String port = settings.getPrivString(Settings.XHTTP_PORT_KEY).trim();
        String username = settings.getPrivString(Settings.USUARIO_KEY).trim();
        String path = ConfigUtils.normalizePath(settings.getPrivString(Settings.XHTTP_PATH_KEY));

        if (server.isEmpty()) return getString(R.string.error_server_required);
        if (!ConfigUtils.isValidPort(port)) return getString(R.string.error_port_invalid);
        if (username.isEmpty()) return getString(R.string.error_username_required);
        if (path.isEmpty()) return getString(R.string.error_path_required);
        if (settings.getVpnDnsForward() && settings.getVpnDnsResolver().trim().isEmpty()) {
            return getString(R.string.error_dns_primary_required);
        }
        if (settings.getVpnUdpForward() && !ConfigUtils.isHostPort(settings.getVpnUdpResolver())) {
            return getString(R.string.error_udpgw_invalid);
        }
        return null;
    }

    private void startTunnel() {
        DemoApplication application = (DemoApplication) getApplication();
        String password = application.getSessionPassword();
        if (password.isEmpty()) {
            Toast.makeText(this, R.string.password_needed_for_session, Toast.LENGTH_LONG).show();
            openSettings();
            return;
        }

        String server = settings.getPrivString(Settings.XHTTP_ENDPOINT_KEY);
        String port = settings.getPrivString(Settings.XHTTP_PORT_KEY);
        String username = settings.getPrivString(Settings.USUARIO_KEY);
        String sni = settings.getPrivString(Settings.CUSTOM_SNI);
        String host = settings.getPrivString(Settings.XHTTP_HOST_KEY);
        String path = ConfigUtils.normalizePath(settings.getPrivString(Settings.XHTTP_PATH_KEY));
        boolean tls = !"0".equals(settings.getPrivString(Settings.XHTTP_TLS_KEY));

        statusView.setText(R.string.status_starting);
        SkStatus.logInfo("Server: " + ConfigUtils.formatHostPort(server, port));
        SkStatus.logInfo("XHTTP: SNI=" + ConfigUtils.displayValue(sni)
                + ", Host=" + ConfigUtils.displayValue(host)
                + ", Path=" + ConfigUtils.displayValue(path)
                + ", TLS=" + (tls ? "on" : "off"));
        SkStatus.logInfo("SSH login: " + username + " (password hidden)");
        TunnelManagerHelper.startXHttpSsh(this, password);
    }

    @Override
    public void updateState(String state, String logMessage, int localizedResId,
                            ConnectionStatus level, Intent intent) {
        runOnUiThread(() -> {
            String label;
            try {
                label = getString(localizedResId);
            } catch (Exception e) {
                label = state;
            }
            statusView.setText(label);
            tunnelActive = level != ConnectionStatus.LEVEL_NOTCONNECTED
                    && level != ConnectionStatus.LEVEL_AUTH_FAILED;
            startStopButton.setText(tunnelActive ? R.string.stop : R.string.start);
        });
    }

    private void requestNotificationPermissionIfNeeded() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
                && ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.POST_NOTIFICATIONS},
                    NOTIFICATION_PERMISSION_REQUEST);
        }
    }
}
