package com.dragonssh.xhttpdemo.core;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ServiceInfo;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;

import androidx.core.app.NotificationCompat;
import androidx.core.app.ServiceCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import com.dragonssh.xhttpdemo.core.logger.ConnectionStatus;
import com.dragonssh.xhttpdemo.core.logger.SkStatus;
import com.dragonssh.xhttpdemo.core.tunnel.TunnelManagerThread;

/** Foreground service that owns the SSH/XHTTP transport. */
public final class XHttpSshService extends Service implements SkStatus.StateListener {
    public static final String NOTIFICATION_CHANNEL_ID = "xhttp_ssh_status";
    public static final int NOTIFICATION_ID = 7001;
    public static final String EXTRA_SSH_PASSWORD = "ssh_password";
    public static final String TUNNEL_SSH_RESTART_SERVICE =
            XHttpSshService.class.getName() + ".RESTART";
    public static final String TUNNEL_SSH_STOP_SERVICE =
            XHttpSshService.class.getName() + ".STOP";

    private final Handler mainHandler = new Handler(Looper.getMainLooper());
    private TunnelManagerThread tunnelManager;
    private Thread tunnelThread;
    private boolean receiverRegistered;

    @Override
    public void onCreate() {
        super.onCreate();
        XHttpSshCore.init(this);
        registerControlReceiver();
        SkStatus.addStateListener(this);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        ServiceCompat.startForeground(this, NOTIFICATION_ID,
                buildNotification(getString(R.string.state_starting)),
                ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
        if (tunnelThread == null || !tunnelThread.isAlive()) {
            String password = intent != null ? intent.getStringExtra(EXTRA_SSH_PASSWORD) : null;
            if (password == null || password.isEmpty()) {
                SkStatus.logError("SSH password was not supplied");
                stopForeground(STOP_FOREGROUND_REMOVE);
                stopSelf(startId);
                return START_NOT_STICKY;
            }
            startTunnel(password);
        }
        return START_NOT_STICKY;
    }

    private synchronized void startTunnel(String password) {
        tunnelManager = new TunnelManagerThread(mainHandler, this, password);
        tunnelManager.setOnStopClientListener(this::finishService);
        tunnelThread = new Thread(tunnelManager, "xhttp-ssh-manager");
        tunnelThread.start();
    }

    private synchronized void stopTunnel() {
        TunnelManagerThread manager = tunnelManager;
        tunnelManager = null;
        if (manager != null) manager.stopAll();
        Thread thread = tunnelThread;
        tunnelThread = null;
        if (thread != null) thread.interrupt();
    }

    private void finishService() {
        mainHandler.post(() -> {
            stopTunnel();
            stopForeground(STOP_FOREGROUND_REMOVE);
            stopSelf();
        });
    }

    @Override
    public void onDestroy() {
        stopTunnel();
        if (receiverRegistered) {
            LocalBroadcastManager.getInstance(this).unregisterReceiver(controlReceiver);
            receiverRegistered = false;
        }
        SkStatus.removeStateListener(this);
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private Notification buildNotification(String text) {
        Intent open = getPackageManager().getLaunchIntentForPackage(getPackageName());
        PendingIntent contentIntent = null;
        if (open != null) {
            open.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
            int flags = PendingIntent.FLAG_UPDATE_CURRENT;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) flags |= PendingIntent.FLAG_IMMUTABLE;
            contentIntent = PendingIntent.getActivity(this, 1, open, flags);
        }

        Intent stop = new Intent(this, MainReceiver.class).setAction(MainReceiver.ACTION_SERVICE_STOP);
        int actionFlags = PendingIntent.FLAG_UPDATE_CURRENT;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) actionFlags |= PendingIntent.FLAG_IMMUTABLE;
        PendingIntent stopIntent = PendingIntent.getBroadcast(this, 2, stop, actionFlags);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_cloud_black_24dp)
                .setContentTitle(getString(R.string.app_name))
                .setContentText(text)
                .setOngoing(true)
                .setOnlyAlertOnce(true)
                .setSilent(true)
                .setCategory(NotificationCompat.CATEGORY_SERVICE)
                .addAction(R.drawable.ic_power_settings_new_black_24dp,
                        getString(R.string.stop), stopIntent);
        if (contentIntent != null) builder.setContentIntent(contentIntent);
        return builder.build();
    }

    @Override
    public void updateState(String state, String logMessage, int localizedResId,
                            ConnectionStatus level, Intent intent) {
        String text;
        try {
            text = getString(localizedResId);
        } catch (Exception ignored) {
            text = state;
        }
        NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        if (nm != null) nm.notify(NOTIFICATION_ID, buildNotification(text));
    }

    private void registerControlReceiver() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(TUNNEL_SSH_STOP_SERVICE);
        filter.addAction(TUNNEL_SSH_RESTART_SERVICE);
        LocalBroadcastManager.getInstance(this).registerReceiver(controlReceiver, filter);
        receiverRegistered = true;
    }

    private final BroadcastReceiver controlReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent == null || intent.getAction() == null) return;
            if (TUNNEL_SSH_STOP_SERVICE.equals(intent.getAction())) {
                finishService();
            } else if (TUNNEL_SSH_RESTART_SERVICE.equals(intent.getAction())) {
                TunnelManagerThread manager = tunnelManager;
                if (manager != null) new Thread(manager::reconnectSSH, "xhttp-manual-reconnect").start();
            }
        }
    };
}
