#!/usr/bin/env bash
# apply_chromium_extension_policy — Escribe de forma idempotente la política managed de Chromium
# que fuerza la instalación de una whitelist de extensiones y bloquea (desinstala) una blocklist
# concreta, sin tocar el resto. Usa ExtensionInstallForcelist + ExtensionInstallBlocklist.

apply_chromium_extension_policy() {
    local policy_path="/etc/chromium/policies/managed/extensions.json"
    local update_url="https://clients2.google.com/service/update2/crx"
    local dry_run=0
    local -a keep_ids=()
    local -a block_ids=()

    # --- Parseo de argumentos ---
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --keep)
                if [[ -z "${2:-}" ]]; then
                    echo "apply_chromium_extension_policy: --keep requiere un ID de extensión" >&2
                    return 1
                fi
                keep_ids+=("$2")
                shift 2
                ;;
            --block)
                if [[ -z "${2:-}" ]]; then
                    echo "apply_chromium_extension_policy: --block requiere un ID de extensión" >&2
                    return 1
                fi
                block_ids+=("$2")
                shift 2
                ;;
            --policy-path)
                if [[ -z "${2:-}" ]]; then
                    echo "apply_chromium_extension_policy: --policy-path requiere un valor" >&2
                    return 1
                fi
                policy_path="$2"
                shift 2
                ;;
            --update-url)
                if [[ -z "${2:-}" ]]; then
                    echo "apply_chromium_extension_policy: --update-url requiere un valor" >&2
                    return 1
                fi
                update_url="$2"
                shift 2
                ;;
            --dry-run)
                dry_run=1
                shift
                ;;
            *)
                echo "apply_chromium_extension_policy: argumento desconocido: $1" >&2
                echo "Uso: apply_chromium_extension_policy [--keep <ext_id>]... [--block <ext_id>]... [--policy-path <path>] [--update-url <url>] [--dry-run]" >&2
                return 1
                ;;
        esac
    done

    # --- Validar que hay al menos una extensión a forzar o bloquear ---
    if [[ ${#keep_ids[@]} -eq 0 && ${#block_ids[@]} -eq 0 ]]; then
        echo "apply_chromium_extension_policy: se requiere al menos un --keep o un --block <ext_id>" >&2
        return 1
    fi

    # --- Construir el JSON ---
    # Dos claves complementarias, ninguna bloquea las extensiones unpacked (--load-extension),
    # de modo que extensiones locales como la de captura de web_proxy siguen cargando:
    #   1. ExtensionInstallForcelist: fuerza la instalación de la whitelist (--keep), que además
    #      no se puede desinstalar desde la UI.
    #   2. ExtensionInstallBlocklist: bloquea Y desinstala las extensiones de la blocklist
    #      (--block) en cualquier perfil. Solo afecta a los IDs listados; el resto no se toca.
    local forcelist_json="[]" blocklist_json="[]"
    if [[ ${#keep_ids[@]} -gt 0 ]]; then
        local entries="" first=1
        for kid in "${keep_ids[@]}"; do
            # Cada --keep puede ser "<id>" (usa el update_url por defecto, Web Store) o
            # "<id>=<update_url>" para una extensión self-hosted (p.ej. file:// a un update.xml local).
            local id="${kid%%=*}" url="$update_url"
            [[ "$kid" == *=* ]] && url="${kid#*=}"
            [[ $first -eq 0 ]] && entries+=","$'\n'
            entries+="    \"${id};${url}\""
            first=0
        done
        forcelist_json=$(printf '[\n%s\n  ]' "$entries")
    fi
    if [[ ${#block_ids[@]} -gt 0 ]]; then
        local entries="" first=1
        for id in "${block_ids[@]}"; do
            [[ $first -eq 0 ]] && entries+=","$'\n'
            entries+="    \"${id}\""
            first=0
        done
        blocklist_json=$(printf '[\n%s\n  ]' "$entries")
    fi

    local new_json
    new_json=$(cat <<JSONEOF
{
  "ExtensionInstallForcelist": ${forcelist_json},
  "ExtensionInstallBlocklist": ${blocklist_json}
}
JSONEOF
)

    # --- Modo dry-run ---
    if [[ $dry_run -eq 1 ]]; then
        echo "[dry-run] JSON que se escribiría en: ${policy_path}"
        echo "---"
        echo "$new_json"
        echo "---"
        echo "[dry-run] No se ha modificado el sistema."
        return 0
    fi

    # --- Idempotencia: comparar con contenido actual ---
    if [[ -f "$policy_path" ]]; then
        local current_content
        current_content=$(cat "$policy_path" 2>/dev/null || true)
        if [[ "$current_content" == "$new_json" ]]; then
            echo "apply_chromium_extension_policy: política ya aplicada (sin cambios). Nada que hacer."
            return 0
        fi
    fi

    # --- Backup del archivo existente ---
    # CRÍTICO: el backup NUNCA puede vivir dentro del directorio de la policy. Chromium lee TODOS
    # los archivos del directorio managed/ (sin filtrar por extensión de nombre), así que un
    # "extensions.json.bak.YYYYMMDD" dentro de managed/ se mergea con la policy efectiva y reinyecta
    # las extensiones del backup. Por eso el backup se guarda en un directorio hermano (policy-backups)
    # que chromium no lee.
    local backup_path=""
    if [[ -f "$policy_path" ]]; then
        local date_suffix policy_dir backup_dir
        date_suffix=$(date +%Y%m%d)
        policy_dir="$(dirname "$policy_path")"
        case "$(basename "$policy_dir")" in
            managed|recommended) backup_dir="$(dirname "$policy_dir")/policy-backups" ;;
            *)                   backup_dir="$policy_dir" ;;
        esac
        backup_path="${backup_dir}/$(basename "$policy_path").bak.${date_suffix}"
        if [[ ! -d "$backup_dir" ]]; then
            if [[ $EUID -ne 0 ]]; then sudo mkdir -p "$backup_dir" 2>/dev/null; else mkdir -p "$backup_dir"; fi
        fi
        if [[ ! -f "$backup_path" ]]; then
            echo "apply_chromium_extension_policy: creando backup → ${backup_path}"
            if [[ $EUID -ne 0 ]]; then
                sudo cp "$policy_path" "$backup_path" || {
                    echo "apply_chromium_extension_policy: no se pudo crear el backup en ${backup_path}" >&2
                    return 1
                }
            else
                cp "$policy_path" "$backup_path" || {
                    echo "apply_chromium_extension_policy: no se pudo crear el backup en ${backup_path}" >&2
                    return 1
                }
            fi
        else
            echo "apply_chromium_extension_policy: backup del día ya existe (${backup_path}), se omite."
        fi
    fi

    # --- Crear directorio padre si no existe ---
    local policy_dir
    policy_dir=$(dirname "$policy_path")
    if [[ ! -d "$policy_dir" ]]; then
        echo "apply_chromium_extension_policy: creando directorio ${policy_dir}"
        if [[ $EUID -ne 0 ]]; then
            sudo mkdir -p "$policy_dir" || {
                echo "apply_chromium_extension_policy: no se pudo crear el directorio ${policy_dir}" >&2
                return 1
            }
        else
            mkdir -p "$policy_dir" || {
                echo "apply_chromium_extension_policy: no se pudo crear el directorio ${policy_dir}" >&2
                return 1
            }
        fi
    fi

    # --- Escribir el JSON vía tmpfile + sudo cp ---
    local tmpfile
    tmpfile=$(mktemp /tmp/chromium_policy_XXXXXX.json)
    echo "$new_json" > "$tmpfile"

    if [[ $EUID -ne 0 ]]; then
        sudo cp "$tmpfile" "$policy_path" || {
            echo "apply_chromium_extension_policy: no se pudo escribir ${policy_path}" >&2
            rm -f "$tmpfile"
            if [[ -n "$backup_path" && -f "$backup_path" ]]; then
                echo "apply_chromium_extension_policy: restaurando backup tras error..."
                sudo cp "$backup_path" "$policy_path" 2>/dev/null || true
            fi
            return 1
        }
    else
        cp "$tmpfile" "$policy_path" || {
            echo "apply_chromium_extension_policy: no se pudo escribir ${policy_path}" >&2
            rm -f "$tmpfile"
            if [[ -n "$backup_path" && -f "$backup_path" ]]; then
                echo "apply_chromium_extension_policy: restaurando backup tras error..."
                cp "$backup_path" "$policy_path" 2>/dev/null || true
            fi
            return 1
        }
    fi
    rm -f "$tmpfile"

    # --- Validar el JSON escrito ---
    local validation_ok=0
    if command -v python3 &>/dev/null; then
        python3 -c "import json,sys; json.load(open(sys.argv[1]))" "$policy_path" 2>/dev/null && validation_ok=1
    elif command -v jq &>/dev/null; then
        jq . "$policy_path" &>/dev/null && validation_ok=1
    else
        validation_ok=1
    fi

    if [[ $validation_ok -eq 0 ]]; then
        echo "apply_chromium_extension_policy: el JSON escrito no es válido — restaurando backup" >&2
        if [[ -n "$backup_path" && -f "$backup_path" ]]; then
            if [[ $EUID -ne 0 ]]; then
                sudo cp "$backup_path" "$policy_path" 2>/dev/null || true
            else
                cp "$backup_path" "$policy_path" 2>/dev/null || true
            fi
        fi
        return 1
    fi

    # --- Resumen final ---
    echo "apply_chromium_extension_policy: política aplicada correctamente."
    echo "  Ruta     : ${policy_path}"
    if [[ ${#keep_ids[@]} -gt 0 ]]; then
        echo "  Forzadas (${#keep_ids[@]}):"
        for id in "${keep_ids[@]}"; do echo "    - ${id}"; done
    fi
    if [[ ${#block_ids[@]} -gt 0 ]]; then
        echo "  Bloqueadas/desinstaladas (${#block_ids[@]}):"
        for id in "${block_ids[@]}"; do echo "    - ${id}"; done
    fi
    echo "  Extensiones unpacked (--load-extension, p.ej. web_proxy): NO afectadas."
    if [[ -n "$backup_path" && -f "$backup_path" ]]; then
        echo "  Backup   : ${backup_path}"
    fi
    echo ""
    echo "  AVISO: Chromium cachea la politica en memoria. Para que surta efecto:"
    echo "    pkill -9 chromium   (cierra TODOS los procesos)"
    echo "    # Luego relanza Chromium. O desde un Chromium abierto:"
    echo "    # chrome://policy → 'Reload policies'"
}

# Auto-ejecución al correr el archivo directo (bash file.sh / fn run). Si se hace `source`,
# solo se define la función y no se ejecuta nada.
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    apply_chromium_extension_policy "$@"
fi
