#!/usr/bin/env python3

# gnucego-repositorio-limpando.py — parte do projeto GNUcego.
# Copyright (C) 2026 GNUcego contributors
#
# Este programa é SOFTWARE LIVRE: você pode redistribuí-lo e/ou modificá-lo
# sob os termos da GNU General Public License (GPL) publicada pela
# Free Software Foundation — versão 3 da Licença, ou (à sua escolha)
# qualquer versão posterior.
#
# Este programa é distribuído na esperança de ser útil, mas SEM QUALQUER
# GARANTIA; sem mesmo a garantia implícita de COMERCIALIZAÇÃO ou de
# ADEQUAÇÃO A UM DETERMINADO FIM. Veja a GNU General Public License
# para mais detalhes.
#
# Você deve ter recebido uma cópia da GNU General Public License junto
# com este programa. Se não, veja <https://www.gnu.org/licenses/>.

"""
gnucego-repositorio-limpando.py — Remove pacotes não-livres (FSDG) do espelho GNUcego.

Lê a lista de pacotes proibidos (gnucego-forbidden-packages.txt), remove os
arquivos .deb/.udeb correspondentes do pool e REGENERA os índices (Packages*)
filtrando as entradas proibidas, depois re-assina os Releases via fix-releases.py.

Uso:
  gnucego-repositorio-limpando.py [--dry-run] [--dist trixie,testing] [--no-sign]

  --dry-run   só lista o que seria removido, não altera nada.
  --no-sign   não roda o fix-releases.py no final (útil p/ testar).

Segurança:
  - Usa 'debmirror' é quem baixa o pool; aqui só REMOVEMOS.
  - Regenera o Packages mantendo os hashes dos pacotes que ficam.
  - Não toca no componente 'extra' (kernel linux-libre, icecat etc. são livres).
"""
import os
import sys
import gzip
import shutil
import subprocess
import glob
import re

MIRROR = "/media/wellton/backup/debian"
FORBIDDEN_LIST = os.path.join(MIRROR, "gnucego", "gnucego-forbidden-packages.txt")
DISTS = ("exu", "ogum")
ARCHES = ("binary-amd64", "binary-all")

DRY_RUN = "--dry-run" in sys.argv
NO_SIGN = "--no-sign" in sys.argv

# extrai --dist=... se presente (senão usa todos)


def parse_dists():
    for a in sys.argv:
        if a.startswith("--dist="):
            return tuple(a.split("=", 1)[1].split(","))
    return DISTS


def load_forbidden(path):
    """Retorna (exact_names, prefix_patterns)."""
    exact, prefix = set(), []
    if not os.path.isfile(path):
        print(f"ERRO: lista não encontrada: {path}")
        sys.exit(1)
    with open(path) as f:
        for line in f:
            line = line.strip()
            if not line or line.startswith("#"):
                continue
            if line.endswith("*"):
                prefix.append(line[:-1])
            else:
                exact.add(line)
    return exact, prefix


def is_forbidden(pkgname, exact, prefix):
    if pkgname in exact:
        return True
    for p in prefix:
        if pkgname.startswith(p):
            return True
    return False


def filter_packages_text(text, exact, prefix):
    """Remove os blocos de pacotes proibidos do texto de um Packages."""
    blocks = text.split("\n\n")
    kept = []
    removed = []
    for b in blocks:
        if not b.strip():
            continue
        m = re.search(r'^Package:\s*(\S+)', b)
        pkg = m.group(1) if m else None
        if pkg and is_forbidden(pkg, exact, prefix):
            removed.append(pkg)
        else:
            kept.append(b)
    return "\n\n".join(kept) + ("\n\n" if kept else ""), removed


def scan_pool_not_used():
    pass  # mantido por compatibilidade


def main():
    exact, prefix = load_forbidden(FORBIDDEN_LIST)
    print(f"Lista de proibidos: {len(exact)} exatos + {len(prefix)} prefixos")
    dists = parse_dists()

    total_removed_files = 0
    total_removed_entries = 0

    for dist in dists:
        for arch in ARCHES:
            pkgdir = os.path.join(MIRROR, "dists", dist, "main", arch)
            if not os.path.isdir(pkgdir):
                continue

            # --- 1) remover .deb/.udeb do pool referenciados por pacotes proibidos ---
            # Lê o Packages atual para saber o Filename de cada pacote proibido.
            for ext in ("Packages.gz", "Packages.xz", "Packages"):
                p = os.path.join(pkgdir, ext)
                if not os.path.isfile(p):
                    continue
                # ler texto
                if ext.endswith(".gz"):
                    with gzip.open(p, "rt", encoding="utf-8", errors="replace") as fh:
                        text = fh.read()
                elif ext.endswith(".xz"):
                    text = subprocess.run(["xz", "-dc", p], capture_output=True).stdout.decode("utf-8", "replace")
                else:
                    with open(p, encoding="utf-8", errors="replace") as fh:
                        text = fh.read()

                # coletar Filename dos proibidos (para remover do pool)
                for b in text.split("\n\n"):
                    if not b.strip():
                        continue
                    m = re.search(r'^Package:\s*(\S+)', b)
                    pkg = m.group(1) if m else None
                    if pkg and is_forbidden(pkg, exact, prefix):
                        fm = re.search(r'^Filename:\s*(\S+)', b, re.M)
                        if fm:
                            fp = os.path.join(MIRROR, fm.group(1))
                            if os.path.isfile(fp):
                                if DRY_RUN:
                                    print(f"[dry-run] removeria .deb: {fm.group(1)}")
                                else:
                                    try:
                                        os.remove(fp)
                                        total_removed_files += 1
                                        print(f"removido .deb: {fm.group(1)}")
                                    except OSError as e:
                                        print(f"  ERRO removendo {fp}: {e}")

        # --- 2) regenerar os Packages (filtrando) em cada arch ---
        for arch in ARCHES:
            pkgdir = os.path.join(MIRROR, "dists", dist, "main", arch)
            if not os.path.isdir(pkgdir):
                continue
            src = os.path.join(pkgdir, "Packages.gz")
            if not os.path.isfile(src):
                continue
            with gzip.open(src, "rt", encoding="utf-8", errors="replace") as fh:
                text = fh.read()
            new_text, removed = filter_packages_text(text, exact, prefix)
            if removed:
                total_removed_entries += len(removed)
                print(f"{dist}/{arch}: {len(removed)} entradas de pacote removidas do índice")
                if DRY_RUN:
                    continue
                # escrever Packages (uncompressed), .gz, .xz
                with open(os.path.join(pkgdir, "Packages"), "w", encoding="utf-8") as fh:
                    fh.write(new_text)
                with gzip.open(os.path.join(pkgdir, "Packages.gz"), "wt", encoding="utf-8") as fh:
                    fh.write(new_text)
                with open(os.path.join(pkgdir, "Packages.xz"), "wb") as fh:
                    fh.write(subprocess.run(["xz", "-c"], input=new_text.encode("utf-8"), capture_output=True).stdout)

    print(f"\nResumo: {total_removed_files} arquivo(s) .deb removidos, {total_removed_entries} entradas de índice regeneradas")

    if DRY_RUN:
        print("[dry-run] Nada foi alterado. Rode sem --dry-run para aplicar.")
        return

    # --- 3) re-assinar os Releases ---
    if not NO_SIGN:
        print("\nRe-assinando Releases (fix-releases.py)...")
        subprocess.run(["python3", "/usr/local/bin/fix-releases.py"])


if __name__ == "__main__":
    main()
