"""
setup/install_tool.py — Zero-touch tool downloader.

Automatically downloads and configures external security tools
like apktool, jadx, adb, etc., directly into ~/tools/ directory.
"""

from __future__ import annotations

import os
import platform
import shutil
import subprocess
import sys
import tarfile
import urllib.request
import zipfile
from pathlib import Path

# ---------------------------------------------------------------------------
# Pip / npm recipes
# ---------------------------------------------------------------------------

_PIP_TOOLS: dict[str, str] = {
    "mcp": "mcp",
    "frida": "frida",
    "frida-tools": "frida-tools",
    "objection": "objection",
    "fridump": "fridump",
    "androguard": "androguard",
    "apkid": "apkid",
    "apkleaks": "apkleaks",
    "quark-engine": "quark-engine",
    "mitmproxy": "mitmproxy",
    "medusa-framework": "medusa-framework",
    "medusa": "medusa-framework",
    "capstone": "capstone",
    "keystone-engine": "keystone-engine",
    "textual": "textual",
    "rich": "rich",
    "tomli": "tomli",
    "hermes-dec": "hermes-dec",
}

_NPM_TOOLS: dict[str, str] = {
    "apk-mitm": "apk-mitm",
    "frida-compile": "frida-compile",
}

# ---------------------------------------------------------------------------
# Downloader framework
# ---------------------------------------------------------------------------

def _get_tools_dir() -> Path:
    d = Path.home() / "tools"
    d.mkdir(parents=True, exist_ok=True)
    return d

def _download_file(url: str, dest: Path) -> None:
    req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
    with urllib.request.urlopen(req) as response, open(dest, 'wb') as out_file:
        shutil.copyfileobj(response, out_file)

def _download_zip(url: str, dest_dir: Path) -> None:
    dest_dir.mkdir(parents=True, exist_ok=True)
    zip_path = dest_dir / "temp.zip"
    _download_file(url, zip_path)
    with zipfile.ZipFile(zip_path, "r") as z:
        z.extractall(dest_dir)
    zip_path.unlink()

def _make_executable(path: Path) -> None:
    if os.name != "nt":
        os.chmod(path, 0o755)

# ---------------------------------------------------------------------------
# Custom Installers
# ---------------------------------------------------------------------------

def _install_apktool() -> dict:
    td = _get_tools_dir()
    jar_url = "https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.9.3.jar"
    jar_dest = td / "apktool.jar"
    
    wrapper_bat_url = "https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/windows/apktool.bat"
    wrapper_sh_url = "https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool"
    
    try:
        _download_file(jar_url, jar_dest)
        if os.name == "nt":
            _download_file(wrapper_bat_url, td / "apktool.bat")
        else:
            sh_dest = td / "apktool"
            _download_file(wrapper_sh_url, sh_dest)
            _make_executable(sh_dest)
        return {"status": "ok", "message": "apktool downloaded successfully"}
    except Exception as e:
        return {"status": "error", "error": str(e)}

def _install_jadx() -> dict:
    td = _get_tools_dir() / "jadx"
    # Jadx 1.4.7 as stable baseline
    url = "https://github.com/skylot/jadx/releases/download/v1.4.7/jadx-1.4.7.zip"
    try:
        if td.exists():
            shutil.rmtree(td)
        _download_zip(url, td)
        if os.name != "nt":
            _make_executable(td / "bin" / "jadx")
        return {"status": "ok", "message": "jadx downloaded successfully"}
    except Exception as e:
        return {"status": "error", "error": str(e)}

def _install_uber_apk_signer() -> dict:
    td = _get_tools_dir() / "signing"
    td.mkdir(parents=True, exist_ok=True)
    # v1.3.0 stable baseline
    url = "https://github.com/patrickfav/uber-apk-signer/releases/download/v1.3.0/uber-apk-signer-1.3.0.jar"
    try:
        _download_file(url, td / "uber-apk-signer.jar")
        return {"status": "ok", "message": "uber-apk-signer downloaded successfully"}
    except Exception as e:
        return {"status": "error", "error": str(e)}

def _install_smali_baksmali() -> dict:
    td = _get_tools_dir()
    smali_url = "https://github.com/JesusFreke/smali/releases/download/v2.5.2/smali-2.5.2.jar"
    baksmali_url = "https://github.com/JesusFreke/smali/releases/download/v2.5.2/baksmali-2.5.2.jar"
    try:
        _download_file(smali_url, td / "smali.jar")
        _download_file(baksmali_url, td / "baksmali.jar")
        return {"status": "ok", "message": "smali/baksmali downloaded successfully"}
    except Exception as e:
        return {"status": "error", "error": str(e)}

def _install_il2cppdumper() -> dict:
    td = _get_tools_dir() / "il2cppdumper"
    url = "https://github.com/Perfare/Il2CppDumper/releases/download/v6.7.40/Il2CppDumper-v6.7.40.zip"
    try:
        _download_zip(url, td)
        return {"status": "ok", "message": "Il2CppDumper downloaded successfully"}
    except Exception as e:
        return {"status": "error", "error": str(e)}

def _install_adb() -> dict:
    td = _get_tools_dir()
    platform_name = platform.system().lower()
    if platform_name == "windows":
        url = "https://dl.google.com/android/repository/platform-tools-latest-windows.zip"
    elif platform_name == "darwin":
        url = "https://dl.google.com/android/repository/platform-tools-latest-darwin.zip"
    else:
        url = "https://dl.google.com/android/repository/platform-tools-latest-linux.zip"
    
    try:
        _download_zip(url, td)
        # It creates a platform-tools folder
        if os.name != "nt":
            _make_executable(td / "platform-tools" / "adb")
        return {"status": "ok", "message": "adb (platform-tools) downloaded successfully"}
    except Exception as e:
        return {"status": "error", "error": str(e)}

def _install_scrcpy() -> dict:
    td = _get_tools_dir() / "scrcpy"
    if os.name == "nt":
        url = "https://github.com/Genymobile/scrcpy/releases/download/v2.4/scrcpy-win64-v2.4.zip"
        try:
            _download_zip(url, td)
            return {"status": "ok", "message": "scrcpy downloaded successfully"}
        except Exception as e:
            return {"status": "error", "error": str(e)}
    else:
        return {
            "status": "manual_required",
            "message": "scrcpy must be installed via package manager directly on Linux/Mac",
            "instructions": "Linux: sudo apt install scrcpy | Mac: brew install scrcpy"
        }

# ---------------------------------------------------------------------------
# Main Routing
# ---------------------------------------------------------------------------

def install_tool(tool_name: str) -> dict:
    """
    Attempt to zero-touch install *tool_name*.
    """
    import shutil

    # 1. Custom Binary Downloaders
    if tool_name == "apktool":
        return _install_apktool()
    if tool_name == "jadx":
        return _install_jadx()
    if tool_name == "uber-apk-signer":
        return _install_uber_apk_signer()
    if tool_name in ("smali", "baksmali"):
        return _install_smali_baksmali()
    if tool_name == "il2cppdumper":
        return _install_il2cppdumper()
    if tool_name == "adb":
        return _install_adb()
    if tool_name == "scrcpy":
        return _install_scrcpy()

    # 2. pip
    pip_pkg = _PIP_TOOLS.get(tool_name)
    if pip_pkg:
        return _pip_install(tool_name, pip_pkg)

    # 3. npm
    npm_pkg = _NPM_TOOLS.get(tool_name)
    if npm_pkg:
        if shutil.which("npm"):
            return _npm_install(tool_name, npm_pkg)
        return {"status": "error", "error": "npm not found. Install Node.js first."}

    return {
        "status": "error",
        "tool": tool_name,
        "error": f"No zero-touch install recipe for '{tool_name}'."
    }

def _pip_install(display_name: str, package: str) -> dict:
    cmd = [sys.executable, "-m", "pip", "install", package]
    try:
        proc = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
        if proc.returncode == 0:
            return {"status": "ok", "message": f"Installed {package}"}
        return {"status": "error", "error": proc.stderr or proc.stdout}
    except Exception as exc:
        return {"status": "error", "error": str(exc)}

def _npm_install(display_name: str, package: str) -> dict:
    cmd = ["npm", "install", "-g", package]
    try:
        proc = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
        if proc.returncode == 0:
            return {"status": "ok", "message": f"npm installed {package}"}
        return {"status": "error", "error": proc.stderr or proc.stdout}
    except Exception as exc:
        return {"status": "error", "error": str(exc)}
