package com.sudoxnym.resumd;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ApplicationInfo;
import android.provider.Settings;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ResumdActivity extends Activity {
    private static final String TAG = "ResumdApp";
    private RecyclerView recyclerView;
    private TextView closeAllButton;
    private ImageView settingsButton;
    private TaskAdapter adapter;
    private List<TaskInfo> tasks = new ArrayList<>();
    private Set<String> pinnedPackages = new HashSet<>();
    private int selectedPosition = 0;
    private boolean closeAllSelected = false;
    private boolean settingsSelected = false;

    // long press detection
    private Handler longPressHandler = new Handler(Looper.getMainLooper());
    private boolean isLongPress = false;
    private boolean keyIsDown = false;
    private static final long LONG_PRESS_DELAY = 600;

    // lift amount in pixels
    private float liftAmountPx;

    // settings
    private static final String PREFS_NAME = "resumd_prefs";
    private SharedPreferences prefs;
    private boolean gridMode = false;
    private int triggerKey = KeyEvent.KEYCODE_BACK;
    private String triggerType = "hold";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e(TAG, "onCreate called");
        setContentView(R.layout.activity_recents);

        // load preferences
        prefs = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
        gridMode = prefs.getBoolean("grid_mode", false);
        triggerKey = prefs.getInt("trigger_key", KeyEvent.KEYCODE_BACK);
        triggerType = prefs.getString("trigger_type", "hold");

        DisplayMetrics dm = getResources().getDisplayMetrics();
        liftAmountPx = 25f * dm.density;

        recyclerView = findViewById(R.id.recyclerView);
        closeAllButton = findViewById(R.id.closeAllButton);
        settingsButton = findViewById(R.id.settingsButton);

        settingsButton.setOnClickListener(v -> showSettingsDialog());

        applyLayoutMode();
        recyclerView.setClipChildren(false);
        recyclerView.setClipToPadding(false);
        recyclerView.setItemAnimator(null);

        View parent = (View) recyclerView.getParent();
        if (parent instanceof ViewGroup) {
            ((ViewGroup) parent).setClipChildren(false);
            ((ViewGroup) parent).setClipToPadding(false);
        }

        loadRunningTasks();

        if (tasks.isEmpty()) {
            Toast.makeText(this, "no running apps", Toast.LENGTH_SHORT).show();
            finish();
            return;
        }

        adapter = new TaskAdapter();
        recyclerView.setAdapter(adapter);

        closeAllButton.setOnClickListener(v -> closeAllApps());
    }

    private void loadRunningTasks() {
        tasks.clear();
        PackageManager pm = getPackageManager();
        LinkedHashSet<String> seen = new LinkedHashSet<>();

        Pattern patternA = Pattern.compile("A=(?:\\d+:)?([a-zA-Z][a-zA-Z0-9_.]+)");
        Pattern patternBase = Pattern.compile("baseActivity=ComponentInfo\\{([a-zA-Z][a-zA-Z0-9_.]+)/");
        Pattern patternTaskId = Pattern.compile("taskId=(\\d+)");

        try {
            Process process = Runtime.getRuntime().exec(new String[]{"su", "-c", "dumpsys activity recents"});
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;

            String currentPackage = null;
            int currentTaskId = -1;

            while ((line = reader.readLine()) != null) {
                // check for task ID line
                Matcher matcherTaskId = patternTaskId.matcher(line);
                if (matcherTaskId.find()) {
                    currentTaskId = Integer.parseInt(matcherTaskId.group(1));

                    // if we have a pending package, add it now with the task ID
                    if (currentPackage != null && !seen.contains(currentPackage)) {
                        if (!currentPackage.equals(getPackageName()) &&
                            !currentPackage.equals("com.sudoxnym.resumd") &&
                            !currentPackage.equals("com.sudoxnym.recents") &&
                            !currentPackage.equals("com.android.systemui") &&
                            !currentPackage.equals("com.android.settings") &&
                            !currentPackage.equals("com.android.tv.settings") &&
                            !currentPackage.startsWith("com.android.providers.") &&
                            !currentPackage.equals("android") &&
                            !currentPackage.equals("com.spocky.projengmenu")) {

                            seen.add(currentPackage);

                            try {
                                ApplicationInfo appInfo = pm.getApplicationInfo(currentPackage, 0);

                                Intent launchIntent = pm.getLaunchIntentForPackage(currentPackage);
                                if (launchIntent == null) {
                                    launchIntent = pm.getLeanbackLaunchIntentForPackage(currentPackage);
                                }
                                if (launchIntent != null) {
                                    TaskInfo ti = new TaskInfo();
                                    ti.packageName = currentPackage;
                                    ti.label = pm.getApplicationLabel(appInfo).toString();
                                    ti.icon = pm.getApplicationIcon(appInfo);
                                    ti.taskId = currentTaskId;
                                    tasks.add(ti);
                                    Log.e(TAG, "added task: " + currentPackage + " taskId=" + currentTaskId);

                                    if (tasks.size() >= 15) break;
                                }
                            } catch (PackageManager.NameNotFoundException e) {}
                        }
                        currentPackage = null;
                        currentTaskId = -1;
                    }
                    continue;
                }

                // check for package name
                String packageName = null;
                Matcher matcherBase = patternBase.matcher(line);
                if (matcherBase.find()) {
                    packageName = matcherBase.group(1);
                } else {
                    Matcher matcherA = patternA.matcher(line);
                    if (matcherA.find() && (line.contains("Recent #") || line.contains("* Task{"))) {
                        packageName = matcherA.group(1);
                    }
                }

                if (packageName != null) {
                    currentPackage = packageName;
                }
            }
            reader.close();
            process.waitFor();
            Log.e(TAG, "loaded " + tasks.size() + " tasks");
        } catch (Exception e) {
            Log.e(TAG, "error loading tasks", e);
        }
    }

    private void updateCloseAllButton() {
        closeAllButton.setSelected(closeAllSelected);
        closeAllButton.setScaleX(closeAllSelected ? 1.1f : 1.0f);
        closeAllButton.setScaleY(closeAllSelected ? 1.1f : 1.0f);
    }

    private void updateSettingsButton() {
        settingsButton.setScaleX(settingsSelected ? 1.3f : 1.0f);
        settingsButton.setScaleY(settingsSelected ? 1.3f : 1.0f);
        settingsButton.setAlpha(settingsSelected ? 1.0f : 0.5f);
    }

    private void applyLayoutMode() {
        if (gridMode) {
            GridLayoutManager glm = new GridLayoutManager(this, 6);
            recyclerView.setLayoutManager(glm);
        } else {
            recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
        }
    }

    private void showSettingsDialog() {
        Log.e(TAG, "showSettingsDialog called");

        View dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_settings, null);

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setView(dialogView);
        AlertDialog dialog = builder.create();

        Window window = dialog.getWindow();
        if (window != null) {
            window.setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
        }

        TextView triggerButtonToggle = dialogView.findViewById(R.id.triggerButtonToggle);
        TextView triggerTypeToggle = dialogView.findViewById(R.id.triggerTypeToggle);
        TextView gridModeToggle = dialogView.findViewById(R.id.gridModeToggle);
        TextView enableAccessibilityButton = dialogView.findViewById(R.id.enableAccessibilityButton);
        TextView checkUpdatesButton = dialogView.findViewById(R.id.checkUpdatesButton);
        TextView backButton = dialogView.findViewById(R.id.backButton);

        // set initial values
        String keyName = KeyEvent.keyCodeToString(triggerKey).replace("KEYCODE_", "").toLowerCase();
        triggerButtonToggle.setText(keyName);
        triggerTypeToggle.setText(triggerType);
        gridModeToggle.setText(gridMode ? "on" : "off");

        triggerButtonToggle.setOnClickListener(v -> {
            triggerButtonToggle.setText("press a button...");
            triggerButtonToggle.setOnKeyListener((view, keyCode, event) -> {
                if (event.getAction() == KeyEvent.ACTION_DOWN) {
                    triggerKey = keyCode;
                    String capturedKeyName = KeyEvent.keyCodeToString(keyCode).replace("KEYCODE_", "").toLowerCase();
                    triggerButtonToggle.setText(capturedKeyName);
                    triggerButtonToggle.setOnKeyListener(null);
                    savePrefs();
                    return true;
                }
                return false;
            });
        });

        triggerTypeToggle.setOnClickListener(v -> {
            triggerType = triggerType.equals("hold") ? "double" : "hold";
            triggerTypeToggle.setText(triggerType);
            savePrefs();
        });

        gridModeToggle.setOnClickListener(v -> {
            gridMode = !gridMode;
            gridModeToggle.setText(gridMode ? "on" : "off");
            applyLayoutMode();
            savePrefs();
        });

        enableAccessibilityButton.setOnClickListener(v -> {
            Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
            Toast.makeText(this, "enable 'resumd' in accessibility services", Toast.LENGTH_LONG).show();
        });

        checkUpdatesButton.setOnClickListener(v -> {
            Toast.makeText(this, "checking for updates...", Toast.LENGTH_SHORT).show();
            checkForUpdates();
            dialog.dismiss();
        });

        backButton.setOnClickListener(v -> dialog.dismiss());

        dialog.show();
    }

    private void savePrefs() {
        prefs.edit()
            .putBoolean("grid_mode", gridMode)
            .putInt("trigger_key", triggerKey)
            .putString("trigger_type", triggerType)
            .apply();
    }

    private void checkForUpdates() {
        // TODO: implement update check from GitHub releases or similar
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
            Toast.makeText(this, "you're on the latest version", Toast.LENGTH_SHORT).show();
        }, 1500);
    }

    private void showAppMenu(TaskInfo task) {
        Log.e(TAG, "showAppMenu called for: " + task.label);
        
        View dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_app_menu, null);
        
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setView(dialogView);
        AlertDialog dialog = builder.create();
        
        Window window = dialog.getWindow();
        if (window != null) {
            window.setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
        }
        
        ImageView menuIcon = dialogView.findViewById(R.id.menuAppIcon);
        TextView titleText = dialogView.findViewById(R.id.menuTitle);
        menuIcon.setImageDrawable(task.icon);
        TextView pinButton = dialogView.findViewById(R.id.pinButton);
        TextView closeButton = dialogView.findViewById(R.id.closeButton);
        
        titleText.setText(task.label);
        
        boolean isPinned = pinnedPackages.contains(task.packageName);
        pinButton.setText(isPinned ? "unpin" : "pin");
        
        pinButton.setOnClickListener(v -> {
            if (isPinned) {
                pinnedPackages.remove(task.packageName);
                Toast.makeText(this, "unpinned " + task.label, Toast.LENGTH_SHORT).show();
            } else {
                pinnedPackages.add(task.packageName);
                Toast.makeText(this, "pinned " + task.label, Toast.LENGTH_SHORT).show();
            }
            adapter.notifyItemChanged(tasks.indexOf(task));
            dialog.dismiss();
        });
        
        closeButton.setOnClickListener(v -> {
            Log.e(TAG, "close button clicked for: " + task.label);
            dialog.dismiss();
            closeApp(task);
        });
        
        dialog.show();
    }

    private void closeApp(TaskInfo task) {
        Log.e(TAG, "closeApp called for: " + task.packageName + " taskId=" + task.taskId);
        Toast.makeText(this, "closing " + task.label + "...", Toast.LENGTH_SHORT).show();

        new Thread(() -> {
            try {
                // force stop the app
                String cmd = "am force-stop " + task.packageName;
                Log.e(TAG, "executing: su -c " + cmd);
                Process process = Runtime.getRuntime().exec(new String[]{"su", "-c", cmd});
                int exitCode = process.waitFor();
                Log.e(TAG, "force-stop exit code: " + exitCode);

                // remove from recents using task ID
                if (task.taskId > 0) {
                    String removeCmd = "am stack remove " + task.taskId;
                    Log.e(TAG, "executing: su -c " + removeCmd);
                    Process removeProcess = Runtime.getRuntime().exec(new String[]{"su", "-c", removeCmd});
                    int removeExitCode = removeProcess.waitFor();
                    Log.e(TAG, "stack remove exit code: " + removeExitCode);
                }

                runOnUiThread(() -> {
                    int index = tasks.indexOf(task);
                    Log.e(TAG, "removing task at index: " + index);
                    if (index >= 0) {
                        tasks.remove(index);
                        pinnedPackages.remove(task.packageName);
                        adapter.notifyItemRemoved(index);
                    }

                    if (tasks.isEmpty()) {
                        Toast.makeText(this, "all apps closed", Toast.LENGTH_SHORT).show();
                        finish();
                        return;
                    }

                    if (selectedPosition >= tasks.size()) {
                        selectedPosition = tasks.size() - 1;
                    }
                    closeAllSelected = false;
                    adapter.notifyItemRangeChanged(0, tasks.size());
                    updateCloseAllButton();
                    Toast.makeText(this, "closed " + task.label, Toast.LENGTH_SHORT).show();
                });
            } catch (Exception e) {
                Log.e(TAG, "failed to close", e);
                runOnUiThread(() -> Toast.makeText(this, "failed: " + e.getMessage(), Toast.LENGTH_SHORT).show());
            }
        }).start();
    }
    
    private void closeAllApps() {
        Log.e(TAG, "closeAllApps called");

        int toClose = 0;
        for (TaskInfo task : tasks) {
            if (!pinnedPackages.contains(task.packageName)) {
                toClose++;
            }
        }

        if (toClose == 0) {
            Toast.makeText(this, "all apps are pinned", Toast.LENGTH_SHORT).show();
            return;
        }

        Toast.makeText(this, "closing " + toClose + " apps...", Toast.LENGTH_SHORT).show();

        new Thread(() -> {
            List<TaskInfo> toRemove = new ArrayList<>();
            for (TaskInfo task : tasks) {
                if (!pinnedPackages.contains(task.packageName)) {
                    try {
                        // force stop the app
                        String cmd = "am force-stop " + task.packageName;
                        Process process = Runtime.getRuntime().exec(new String[]{"su", "-c", cmd});
                        process.waitFor();

                        // remove from recents using task ID
                        if (task.taskId > 0) {
                            String removeCmd = "am stack remove " + task.taskId;
                            Process removeProcess = Runtime.getRuntime().exec(new String[]{"su", "-c", removeCmd});
                            removeProcess.waitFor();
                        }

                        toRemove.add(task);
                    } catch (Exception e) {
                        Log.e(TAG, "error closing " + task.packageName, e);
                    }
                }
            }
            runOnUiThread(() -> {
                tasks.removeAll(toRemove);
                adapter.notifyDataSetChanged();

                if (tasks.isEmpty()) {
                    Toast.makeText(this, "all apps closed", Toast.LENGTH_SHORT).show();
                    finish();
                } else {
                    if (selectedPosition >= tasks.size()) {
                        selectedPosition = tasks.size() - 1;
                    }
                    Toast.makeText(this, "closed " + toRemove.size() + " apps", Toast.LENGTH_SHORT).show();
                }
            });
        }).start();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.e(TAG, "onKeyDown: " + keyCode + " repeat=" + event.getRepeatCount());
        
        if (tasks.isEmpty()) {
            finish();
            return true;
        }
        
        // ignore repeat events
        if (event.getRepeatCount() > 0) {
            return true;
        }

        switch (keyCode) {
            case KeyEvent.KEYCODE_DPAD_CENTER:
            case KeyEvent.KEYCODE_ENTER:
                if (settingsSelected) {
                    showSettingsDialog();
                    return true;
                }
                if (closeAllSelected) {
                    closeAllApps();
                    return true;
                }
                // start long press timer
                keyIsDown = true;
                isLongPress = false;
                final TaskInfo taskForMenu = tasks.get(selectedPosition);
                longPressHandler.postDelayed(() -> {
                    if (keyIsDown) {
                        isLongPress = true;
                        Log.e(TAG, "LONG PRESS detected for: " + taskForMenu.label);
                        showAppMenu(taskForMenu);
                    }
                }, LONG_PRESS_DELAY);
                return true;
                
            case KeyEvent.KEYCODE_DPAD_LEFT:
                if (settingsSelected || closeAllSelected) {
                    // ignore left/right when on settings or close all
                } else if (selectedPosition > 0) {
                    int oldPos = selectedPosition;
                    selectedPosition--;
                    adapter.notifyItemChanged(oldPos);
                    adapter.notifyItemChanged(selectedPosition);
                    recyclerView.smoothScrollToPosition(selectedPosition);
                }
                return true;

            case KeyEvent.KEYCODE_DPAD_RIGHT:
                if (settingsSelected || closeAllSelected) {
                    // ignore left/right when on settings or close all
                } else if (selectedPosition < tasks.size() - 1) {
                    int oldPos = selectedPosition;
                    selectedPosition++;
                    adapter.notifyItemChanged(oldPos);
                    adapter.notifyItemChanged(selectedPosition);
                    recyclerView.smoothScrollToPosition(selectedPosition);
                }
                return true;

            case KeyEvent.KEYCODE_DPAD_DOWN:
                if (settingsSelected) {
                    settingsSelected = false;
                    updateSettingsButton();
                    adapter.notifyItemRangeChanged(0, tasks.size());
                } else if (gridMode && !closeAllSelected) {
                    // grid mode: move down one row (6 columns)
                    int newPos = selectedPosition + 6;
                    if (newPos < tasks.size()) {
                        int oldPos = selectedPosition;
                        selectedPosition = newPos;
                        adapter.notifyItemChanged(oldPos);
                        adapter.notifyItemChanged(selectedPosition);
                        recyclerView.smoothScrollToPosition(selectedPosition);
                    } else {
                        // at bottom row, go to close all
                        closeAllSelected = true;
                        updateCloseAllButton();
                        adapter.notifyItemRangeChanged(0, tasks.size());
                    }
                } else if (!closeAllSelected) {
                    closeAllSelected = true;
                    updateCloseAllButton();
                    adapter.notifyItemRangeChanged(0, tasks.size());
                }
                return true;

            case KeyEvent.KEYCODE_DPAD_UP:
                if (closeAllSelected) {
                    closeAllSelected = false;
                    updateCloseAllButton();
                    adapter.notifyItemRangeChanged(0, tasks.size());
                } else if (gridMode && !settingsSelected) {
                    // grid mode: move up one row (6 columns)
                    int newPos = selectedPosition - 6;
                    if (newPos >= 0) {
                        int oldPos = selectedPosition;
                        selectedPosition = newPos;
                        adapter.notifyItemChanged(oldPos);
                        adapter.notifyItemChanged(selectedPosition);
                        recyclerView.smoothScrollToPosition(selectedPosition);
                    } else {
                        // at top row, go to settings
                        settingsSelected = true;
                        updateSettingsButton();
                        adapter.notifyItemRangeChanged(0, tasks.size());
                    }
                } else if (!settingsSelected) {
                    settingsSelected = true;
                    updateSettingsButton();
                    adapter.notifyItemRangeChanged(0, tasks.size());
                }
                return true;
                
            case KeyEvent.KEYCODE_BACK:
                finish();
                return true;
        }
        return super.onKeyDown(keyCode, event);
    }
    
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        Log.e(TAG, "onKeyUp: " + keyCode + " isLongPress=" + isLongPress);
        
        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
            keyIsDown = false;
            longPressHandler.removeCallbacksAndMessages(null);
            
            // quick click = launch app (only if not long press and on app row)
            if (!isLongPress && !closeAllSelected && !settingsSelected) {
                if (selectedPosition < tasks.size()) {
                    Log.e(TAG, "quick click - launching: " + tasks.get(selectedPosition).label);
                    launchApp(tasks.get(selectedPosition));
                }
            }
            return true;
        }
        return super.onKeyUp(keyCode, event);
    }

    private void launchApp(TaskInfo task) {
        Log.e(TAG, "launchApp: " + task.packageName);
        Intent launchIntent = getPackageManager().getLaunchIntentForPackage(task.packageName);
        if (launchIntent == null) {
            launchIntent = getPackageManager().getLeanbackLaunchIntentForPackage(task.packageName);
        }
        if (launchIntent != null) {
            launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
            startActivity(launchIntent);
        }
        finish();
    }

    class TaskInfo {
        String packageName;
        String label;
        Drawable icon;
        int taskId = -1;
    }

    class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.ViewHolder> {

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_task, parent, false);
            return new ViewHolder(v);
        }

        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            TaskInfo task = tasks.get(position);
            holder.icon.setImageDrawable(task.icon);
            holder.label.setText(task.label);

            boolean isSelected = !closeAllSelected && !settingsSelected && (position == selectedPosition);
            boolean isPinned = pinnedPackages.contains(task.packageName);
            
            holder.itemView.setSelected(isSelected);
            holder.itemView.setAlpha(isSelected ? 1.0f : 0.7f);
            holder.itemView.setScaleX(isSelected ? 1.05f : 1.0f);
            holder.itemView.setScaleY(isSelected ? 1.05f : 1.0f);
            holder.itemView.setTranslationY(isSelected ? -liftAmountPx : 0f);
            
            if (isPinned) {
                holder.label.setText("\uD83D\uDCCC " + task.label);
            }
            
            holder.itemView.invalidate();
        }

        @Override
        public int getItemCount() {
            return tasks.size();
        }

        class ViewHolder extends RecyclerView.ViewHolder {
            ImageView icon;
            TextView label;

            ViewHolder(View v) {
                super(v);
                icon = v.findViewById(R.id.taskIcon);
                label = v.findViewById(R.id.taskLabel);
            }
        }
    }
}
