#!/usr/bin/env bash
#
# web_proxy — proxy de interceptacion HTTP/HTTPS liviano, siempre activo.
#
# Orquesta funciones del registry fn_registry para capturar todo el trafico de
# un navegador (estilo ZAP/Burp) con un footprint minimo basado en mitmproxy:
#
#   start_mitm_capture_bash_cybersecurity   arranca mitmdump con rotacion
#   rotate_capture_flows_py_cybersecurity   addon que trocea las capturas
#   query_mitm_flows_bash_cybersecurity     consulta capturas guardadas
#   launch_chromium_proxy_bash_browser      navegador proxeado en perfil aislado
#
# El proxy puede correr en dos modos:
#   - manual:  `web_proxy start` / `web_proxy stop` (background + pidfile)
#   - servicio: `web_proxy install-service` (systemd --user, Restart=always,
#               siempre activo, sobrevive a logout/reboot con linger)
#
# Estado y configuracion viven en $WEB_PROXY_HOME (default ~/.web_proxy).
# Las capturas .mitm viven en el directorio de salida (default ~/captures).

set -uo pipefail

# --- Resolucion de rutas -----------------------------------------------------

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# apps/web_proxy/ -> raiz del registry son dos niveles arriba.
REGISTRY_ROOT="${FN_REGISTRY_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
export FN_REGISTRY_ROOT="$REGISTRY_ROOT"

FN_BIN="$REGISTRY_ROOT/fn"
ADDON_PATH="$REGISTRY_ROOT/python/functions/cybersecurity/rotate_capture_flows.py"

START_FN="$REGISTRY_ROOT/bash/functions/cybersecurity/start_mitm_capture.sh"
QUERY_FN="$REGISTRY_ROOT/bash/functions/cybersecurity/query_mitm_flows.sh"
BROWSER_FN="$REGISTRY_ROOT/bash/functions/browser/launch_chromium_proxy.sh"

# --- Configuracion -----------------------------------------------------------

WEB_PROXY_HOME="${WEB_PROXY_HOME:-$HOME/.web_proxy}"
PIDFILE="$WEB_PROXY_HOME/web_proxy.pid"
CONFFILE="$WEB_PROXY_HOME/web_proxy.conf"
SERVICE_NAME="web_proxy.service"
SYSTEMD_USER_DIR="$HOME/.config/systemd/user"

DEFAULT_PORT=8080
DEFAULT_OUT="$HOME/captures"
DEFAULT_ROTATE=20

MITMDUMP_BIN="$(command -v mitmdump 2>/dev/null || echo "$HOME/.local/bin/mitmdump")"
MITMWEB_BIN="$(command -v mitmweb 2>/dev/null || echo "$HOME/.local/bin/mitmweb")"
CA_CERT="$HOME/.mitmproxy/mitmproxy-ca-cert.pem"

mkdir -p "$WEB_PROXY_HOME"

# --- Helpers -----------------------------------------------------------------

err()  { printf '\033[31m%s\033[0m\n' "$*" >&2; }
ok()   { printf '\033[32m%s\033[0m\n' "$*"; }
info() { printf '%s\n' "$*"; }

# Lee KEY del conffile (formato KEY=VAL), o imprime el default pasado.
conf_get() {
    local key="$1" def="${2:-}"
    if [[ -f "$CONFFILE" ]]; then
        local val
        val="$(grep -E "^${key}=" "$CONFFILE" 2>/dev/null | tail -1 | cut -d= -f2-)"
        [[ -n "$val" ]] && { printf '%s' "$val"; return; }
    fi
    printf '%s' "$def"
}

conf_write() {
    local port="$1" out="$2" rotate="$3"
    cat > "$CONFFILE" <<EOF
PORT=$port
OUT=$out
ROTATE=$rotate
EOF
}

# PID del proxy manual, si vive. Imprime el PID o nada.
running_pid() {
    [[ -f "$PIDFILE" ]] || return 1
    local pid
    pid="$(cat "$PIDFILE" 2>/dev/null)"
    [[ -n "$pid" ]] || return 1
    if kill -0 "$pid" 2>/dev/null; then
        printf '%s' "$pid"
        return 0
    fi
    return 1
}

service_active() {
    systemctl --user is-active --quiet "$SERVICE_NAME" 2>/dev/null
}

# --- Subcomandos -------------------------------------------------------------

cmd_start() {
    local port out rotate
    port="$DEFAULT_PORT"; out="$DEFAULT_OUT"; rotate="$DEFAULT_ROTATE"
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --port)       port="$2"; shift 2 ;;
            --out)        out="$2"; shift 2 ;;
            --rotate-min) rotate="$2"; shift 2 ;;
            *) err "start: flag desconocido: $1"; return 2 ;;
        esac
    done

    if service_active; then
        err "El servicio systemd ($SERVICE_NAME) ya esta activo. Usa 'web_proxy stop-service' o gestiona el servicio en su lugar."
        return 1
    fi
    if running_pid >/dev/null; then
        err "Ya hay un proxy manual corriendo (PID $(running_pid)). Para con 'web_proxy stop'."
        return 1
    fi

    local json
    json="$(bash "$START_FN" --port "$port" --out "$out" --rotate-min "$rotate")" || {
        err "Fallo al arrancar mitmdump."
        return 1
    }
    local pid
    pid="$(printf '%s' "$json" | python3 -c 'import sys,json;print(json.load(sys.stdin)["pid"])' 2>/dev/null)"
    if [[ -z "$pid" ]]; then
        err "No se pudo extraer el PID del arranque. Salida: $json"
        return 1
    fi
    printf '%s' "$pid" > "$PIDFILE"
    conf_write "$port" "$out" "$rotate"
    ok "Proxy activo en 127.0.0.1:$port (PID $pid)"
    info "  capturas -> $out (rotacion cada ${rotate} min)"
    info "  navegador -> web_proxy browser"
    info "  consultar -> web_proxy query \"~m POST\""
}

cmd_stop() {
    local pid
    if pid="$(running_pid)"; then
        kill -TERM "$pid" 2>/dev/null
        # Esperar cierre limpio; SIGKILL si se resiste.
        for _ in 1 2 3 4 5; do
            kill -0 "$pid" 2>/dev/null || break
            sleep 0.3
        done
        kill -9 "$pid" 2>/dev/null
        rm -f "$PIDFILE"
        ok "Proxy manual detenido (PID $pid)."
    else
        rm -f "$PIDFILE"
        info "No habia proxy manual corriendo."
    fi
}

cmd_restart() {
    cmd_stop
    sleep 1
    cmd_start "$@"
}

cmd_status() {
    local port out rotate
    port="$(conf_get PORT "$DEFAULT_PORT")"
    out="$(conf_get OUT "$DEFAULT_OUT")"
    rotate="$(conf_get ROTATE "$DEFAULT_ROTATE")"

    info "web_proxy — estado"
    info "  home:      $WEB_PROXY_HOME"
    info "  capturas:  $out"
    info "  rotacion:  cada ${rotate} min"

    local pid
    if pid="$(running_pid)"; then
        ok  "  manual:    ACTIVO (PID $pid, puerto $port)"
    else
        info "  manual:    parado"
    fi

    if service_active; then
        ok  "  servicio:  ACTIVO ($SERVICE_NAME)"
    elif systemctl --user list-unit-files "$SERVICE_NAME" &>/dev/null && \
         systemctl --user cat "$SERVICE_NAME" &>/dev/null; then
        info "  servicio:  instalado, parado"
    else
        info "  servicio:  no instalado"
    fi

    if [[ -d "$out" ]]; then
        local n size
        n="$(find "$out" -maxdepth 1 -name 'traffic-*.mitm' 2>/dev/null | wc -l)"
        size="$(du -sh "$out" 2>/dev/null | cut -f1)"
        info "  archivos:  ${n} capturas (${size:-0})"
        local last
        last="$(find "$out" -maxdepth 1 -name 'traffic-*.mitm' -printf '%T@ %p\n' 2>/dev/null | sort -nr | head -1 | cut -d' ' -f2-)"
        [[ -n "$last" ]] && info "  ultima:    $(basename "$last")"
    fi

    if [[ -f "$CA_CERT" ]]; then
        info "  CA HTTPS:  $CA_CERT"
    else
        info "  CA HTTPS:  no generado todavia (arranca el proxy una vez)"
    fi
}

cmd_browser() {
    local url="" proxy_port
    proxy_port="$(conf_get PORT "$DEFAULT_PORT")"
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --url)  url="$2"; shift 2 ;;
            --port) proxy_port="$2"; shift 2 ;;
            *) err "browser: flag desconocido: $1"; return 2 ;;
        esac
    done

    if ! running_pid >/dev/null && ! service_active; then
        err "No hay proxy activo. Arranca primero con 'web_proxy start' o instala el servicio."
        return 1
    fi

    local args=(--proxy "http://127.0.0.1:${proxy_port}" --profile "$WEB_PROXY_HOME/chromium-profile")
    # Si el CA esta instalado en el perfil, no forzamos --ignore-certificate-errors.
    [[ -n "$url" ]] && args+=(--url "$url")
    bash "$BROWSER_FN" "${args[@]}"
}

# Resuelve la lista de archivos a consultar: por defecto la ultima captura.
resolve_capture_files() {
    local out scope="$1"
    out="$(conf_get OUT "$DEFAULT_OUT")"
    if [[ "$scope" == "all" ]]; then
        find "$out" -maxdepth 1 -name 'traffic-*.mitm' 2>/dev/null | sort
    else
        find "$out" -maxdepth 1 -name 'traffic-*.mitm' -printf '%T@ %p\n' 2>/dev/null \
            | sort -nr | head -1 | cut -d' ' -f2-
    fi
}

cmd_query() {
    local filter="" scope="last"
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --all)  scope="all"; shift ;;
            --last) scope="last"; shift ;;
            *)      filter="$1"; shift ;;
        esac
    done
    local files
    mapfile -t files < <(resolve_capture_files "$scope")
    if [[ ${#files[@]} -eq 0 || -z "${files[0]}" ]]; then
        err "No hay capturas en $(conf_get OUT "$DEFAULT_OUT")."
        return 1
    fi
    if [[ -n "$filter" ]]; then
        bash "$QUERY_FN" "${files[@]}" --filter "$filter"
    else
        bash "$QUERY_FN" "${files[@]}"
    fi
}

cmd_har() {
    local outhar="${1:-$HOME/captures/export.har}" scope="last"
    [[ "${2:-}" == "--all" ]] && scope="all"
    local files
    mapfile -t files < <(resolve_capture_files "$scope")
    if [[ ${#files[@]} -eq 0 || -z "${files[0]}" ]]; then
        err "No hay capturas para exportar."
        return 1
    fi
    bash "$QUERY_FN" "${files[@]}" --har "$outhar"
    ok "HAR exportado a $outhar"
}

cmd_inspect() {
    local file="${1:-}"
    if [[ -z "$file" ]]; then
        file="$(resolve_capture_files last)"
    fi
    if [[ -z "$file" || ! -f "$file" ]]; then
        err "No hay captura para inspeccionar. Pasa una ruta o arranca el proxy."
        return 1
    fi
    info "Abriendo mitmweb sobre $(basename "$file") -> http://127.0.0.1:8081"
    info "(Ctrl-C para cerrar la UI; no afecta al proxy activo)"
    "$MITMWEB_BIN" -r "$file" --no-web-open-browser
}

cmd_ca() {
    info "HTTPS via proxy de interceptacion requiere confiar en el CA de mitmproxy."
    info ""
    if [[ ! -f "$CA_CERT" ]]; then
        err "El CA aun no existe ($CA_CERT)."
        info "Arranca el proxy una vez ('web_proxy start') para que mitmproxy lo genere."
        return 1
    fi
    info "CA generado en: $CA_CERT"
    info ""
    info "Opciones:"
    info "  1. Confianza a nivel sistema (recomendado):"
    info "       sudo cp $CA_CERT /usr/local/share/ca-certificates/mitmproxy.crt"
    info "       sudo update-ca-certificates"
    info "  2. Solo para el navegador proxeado: importa el .pem en"
    info "     chrome://settings/certificates (pestaña Autoridades)."
    info "  3. Rapido y sucio (menos seguro): el navegador de 'web_proxy browser'"
    info "     ya usa --ignore-certificate-errors si no instalas el CA."
}

# Genera e instala el unit systemd --user. El servicio corre mitmdump en
# foreground (systemd gestiona el proceso) con Restart=always.
cmd_install_service() {
    local port out rotate enable_linger="no"
    port="$(conf_get PORT "$DEFAULT_PORT")"
    out="$(conf_get OUT "$DEFAULT_OUT")"
    rotate="$(conf_get ROTATE "$DEFAULT_ROTATE")"
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --port)       port="$2"; shift 2 ;;
            --out)        out="$2"; shift 2 ;;
            --rotate-min) rotate="$2"; shift 2 ;;
            --linger)     enable_linger="yes"; shift ;;
            *) err "install-service: flag desconocido: $1"; return 2 ;;
        esac
    done

    if [[ ! -x "$MITMDUMP_BIN" ]]; then
        err "mitmdump no encontrado. Instala con: uv tool install mitmproxy"
        return 1
    fi

    # Si hay un proxy manual corriendo, pararlo para no chocar de puerto.
    if running_pid >/dev/null; then
        info "Parando el proxy manual antes de instalar el servicio..."
        cmd_stop
    fi

    mkdir -p "$SYSTEMD_USER_DIR" "$out"
    conf_write "$port" "$out" "$rotate"

    cat > "$SYSTEMD_USER_DIR/$SERVICE_NAME" <<EOF
[Unit]
Description=web_proxy — proxy de interceptacion HTTP/HTTPS liviano (mitmproxy)
After=network.target

[Service]
Type=simple
ExecStart=$MITMDUMP_BIN -s $ADDON_PATH --set rotate_min=$rotate --set capture_dir=$out --listen-port $port
Restart=always
RestartSec=2
# Restart=always (no on-failure): un SIGTERM limpio es exit success y
# on-failure NO reiniciaria, dejando el proxy muerto en silencio.

[Install]
WantedBy=default.target
EOF

    systemctl --user daemon-reload
    systemctl --user enable --now "$SERVICE_NAME"

    if [[ "$enable_linger" == "yes" ]]; then
        loginctl enable-linger "$USER" 2>/dev/null \
            && ok "Linger activado: el servicio sigue vivo aunque cierres sesion." \
            || err "No se pudo activar linger (requiere permisos). El servicio para al cerrar sesion."
    fi

    sleep 1
    if service_active; then
        ok "Servicio instalado y ACTIVO en 127.0.0.1:$port."
        info "  capturas -> $out (rotacion cada ${rotate} min)"
        info "  logs      -> web_proxy logs"
        info "  navegador -> web_proxy browser"
        [[ "$enable_linger" == "no" ]] && info "  persistir tras logout -> web_proxy install-service --linger"
    else
        err "El servicio no arranco. Revisa: web_proxy logs"
        return 1
    fi
}

cmd_uninstall_service() {
    systemctl --user disable --now "$SERVICE_NAME" 2>/dev/null
    rm -f "$SYSTEMD_USER_DIR/$SERVICE_NAME"
    systemctl --user daemon-reload
    ok "Servicio $SERVICE_NAME desinstalado."
}

cmd_logs() {
    if service_active || systemctl --user cat "$SERVICE_NAME" &>/dev/null; then
        journalctl --user -u "$SERVICE_NAME" -n "${1:-40}" --no-pager
    else
        local out
        out="$(conf_get OUT "$DEFAULT_OUT")"
        [[ -f "$out/mitmdump.log" ]] && tail -n "${1:-40}" "$out/mitmdump.log" \
            || info "No hay logs (servicio no instalado, sin log manual)."
    fi
}

usage() {
    cat <<'EOF'
web_proxy — proxy de interceptacion HTTP/HTTPS liviano, siempre activo (mitmproxy)

USO: web_proxy <comando> [opciones]

Proxy:
  start [--port N] [--out DIR] [--rotate-min N]   Arranca el proxy (background, manual)
  stop                                            Para el proxy manual
  restart [opciones de start]                     Reinicia el proxy manual
  status                                          Estado: proxy, servicio, capturas, CA

Servicio (siempre activo, systemd --user):
  install-service [--port N] [--out DIR] [--rotate-min N] [--linger]
                                                  Instala + arranca como servicio
  stop-service / uninstall-service                Para / desinstala el servicio
  logs [N]                                        Ultimas N lineas de log

Navegacion:
  browser [--url URL] [--port N]                  Lanza Chromium proxeado (perfil aislado)
  ca                                              Instrucciones para confiar en el CA (HTTPS)

Consultar capturas:
  query [FILTRO] [--all|--last]                   Vuelca flujos (default: ultima captura)
                                                  Filtros mitmproxy: "~m POST", "~c 500", "~d host"
  har [SALIDA.har] [--all]                        Exporta a HAR
  inspect [ARCHIVO.mitm]                          Abre UI web (mitmweb) sobre una captura

Config:
  WEB_PROXY_HOME   directorio de estado (default ~/.web_proxy)
  Defaults: puerto 8080, capturas ~/captures, rotacion 20 min

Ejemplo completo:
  web_proxy install-service --port 8080 --out ~/captures --rotate-min 20
  web_proxy ca                 # confiar en el CA para HTTPS
  web_proxy browser            # navega; todo queda capturado
  web_proxy query "~m POST"    # revisa los POST de la ultima ventana
  web_proxy inspect            # UI visual de la ultima captura
EOF
}

# --- Dispatch ----------------------------------------------------------------

main() {
    local cmd="${1:-help}"
    shift || true
    case "$cmd" in
        start)              cmd_start "$@" ;;
        stop)               cmd_stop "$@" ;;
        restart)            cmd_restart "$@" ;;
        status)             cmd_status "$@" ;;
        browser)            cmd_browser "$@" ;;
        query)              cmd_query "$@" ;;
        har)                cmd_har "$@" ;;
        inspect)            cmd_inspect "$@" ;;
        ca)                 cmd_ca "$@" ;;
        install-service)    cmd_install_service "$@" ;;
        stop-service)       systemctl --user stop "$SERVICE_NAME" && ok "Servicio parado." ;;
        uninstall-service)  cmd_uninstall_service "$@" ;;
        logs)               cmd_logs "$@" ;;
        help|-h|--help)     usage ;;
        *) err "Comando desconocido: $cmd"; echo; usage; return 2 ;;
    esac
}

main "$@"
