// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause

package com.tailscale.ipn;

import android.os.Build;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.VpnService;
import android.system.OsConstants;
import androidx.work.WorkManager;
import androidx.work.OneTimeWorkRequest;

import org.gioui.GioActivity;

import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;

public class IPNService extends VpnService {
	public static final String ACTION_REQUEST_VPN = "com.tailscale.ipn.REQUEST_VPN";
	public static final String ACTION_STOP_VPN = "com.tailscale.ipn.STOP_VPN";

	@Override public int onStartCommand(Intent intent, int flags, int startId) {
		if (intent != null && ACTION_STOP_VPN.equals(intent.getAction())) {
			((App)getApplicationContext()).autoConnect = false;
			close();
			return START_NOT_STICKY;
		} 
		if (intent != null && "android.net.VpnService".equals(intent.getAction())) {
			// Start VPN and connect to it due to Always-on VPN
			Intent i = new Intent(IPNReceiver.INTENT_CONNECT_VPN);
			i.setPackage(getPackageName());
			i.setClass(getApplicationContext(), com.tailscale.ipn.IPNReceiver.class);
			sendBroadcast(i);
			requestVPN();
			connect();
			return START_STICKY;
		}
		requestVPN();
		App app = ((App)getApplicationContext());
		if (app.vpnReady && app.autoConnect) {
			connect();
		}
		return START_STICKY;
	}

	private void close() {
		stopForeground(true);
		disconnect();
	}

	@Override public void onDestroy() {
		close();
		super.onDestroy();
	}

	@Override public void onRevoke() {
		close();
		super.onRevoke();
	}

	private PendingIntent configIntent() {
		return PendingIntent.getActivity(this, 0, new Intent(this, IPNActivity.class),
			PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
	}

	private void disallowApp(VpnService.Builder b, String name) {
		try {
			b.addDisallowedApplication(name);
		} catch (PackageManager.NameNotFoundException e) {
			return;
		}
	}

	protected VpnService.Builder newBuilder() {
		VpnService.Builder b = new VpnService.Builder()
			.setConfigureIntent(configIntent())
			.allowFamily(OsConstants.AF_INET)
			.allowFamily(OsConstants.AF_INET6);
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
			b.setMetered(false); // Inherit the metered status from the underlying networks.
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
			b.setUnderlyingNetworks(null); // Use all available networks.

		// RCS/Jibe https://github.com/tailscale/tailscale/issues/2322
		this.disallowApp(b, "com.google.android.apps.messaging");

		// Stadia https://github.com/tailscale/tailscale/issues/3460
		this.disallowApp(b, "com.google.stadia.android");

		// Android Auto https://github.com/tailscale/tailscale/issues/3828
		this.disallowApp(b, "com.google.android.projection.gearhead");

		// GoPro https://github.com/tailscale/tailscale/issues/2554
		this.disallowApp(b, "com.gopro.smarty");

		// Sonos https://github.com/tailscale/tailscale/issues/2548
		this.disallowApp(b, "com.sonos.acr");
		this.disallowApp(b, "com.sonos.acr2");

		// Google Chromecast https://github.com/tailscale/tailscale/issues/3636
		this.disallowApp(b, "com.google.android.apps.chromecast.app");

		return b;
	}

	public void notify(String title, String message) {
		NotificationCompat.Builder builder = new NotificationCompat.Builder(this, App.NOTIFY_CHANNEL_ID)
			.setSmallIcon(R.drawable.ic_notification)
			.setContentTitle(title)
			.setContentText(message)
			.setContentIntent(configIntent())
			.setAutoCancel(true)
			.setOnlyAlertOnce(true)
			.setPriority(NotificationCompat.PRIORITY_DEFAULT);

		NotificationManagerCompat nm = NotificationManagerCompat.from(this);
		nm.notify(App.NOTIFY_NOTIFICATION_ID, builder.build());
	}

	public void updateStatusNotification(String title, String message) {
		NotificationCompat.Builder builder = new NotificationCompat.Builder(this, App.STATUS_CHANNEL_ID)
			.setSmallIcon(R.drawable.ic_notification)
			.setContentTitle(title)
			.setContentText(message)
			.setContentIntent(configIntent())
			.setPriority(NotificationCompat.PRIORITY_LOW);

		startForeground(App.STATUS_NOTIFICATION_ID, builder.build());
	}

	private native void requestVPN();

	private native void disconnect();
	private native void connect();
}
