#!/usr/bin/env python3
"""Fix EPUB metadata in-place by patching content.opf inside the ZIP.
Adapt the `fixes` list at the bottom for each batch of books.
Run with: sudo python3 fix_epub_metadata.py
"""
import zipfile, os, xml.etree.ElementTree as ET

BOOKS_DIR = "/media/HDD_1TB/Medien/Bücher"
NS = {
    "opf": "http://www.idpf.org/2007/opf",
    "dc": "http://purl.org/dc/elements/1.1/",
}

for prefix, uri in NS.items():
    ET.register_namespace(prefix, uri)

def find_opf_path(zf):
    with zf.open("META-INF/container.xml") as f:
        root = ET.fromstring(f.read().decode("utf-8"))
    ns_c = {"c": "urn:oasis:names:tc:opendocument:xmlns:container"}
    return root.find(".//c:rootfile", ns_c).get("full-path")

def set_or_add(parent, tag, text):
    elem = parent.find(f"{{http://purl.org/dc/elements/1.1/}}{tag}")
    if elem is None:
        elem = ET.SubElement(parent, f"{{http://purl.org/dc/elements/1.1/}}{tag}")
    elem.text = text

def fix_epub(path, fixes):
    tmp_path = path + ".tmp"
    with zipfile.ZipFile(path, 'r') as zin:
        opf_path = find_opf_path(zin)
        opf_root = ET.fromstring(zin.read(opf_path).decode("utf-8"))
        metadata = opf_root.find("{http://www.idpf.org/2007/opf}metadata")

        changes = []
        for tag, new_value in fixes.get("set", {}).items():
            old = metadata.find(f"{{http://purl.org/dc/elements/1.1/}}{tag}")
            old_val = old.text if old is not None else "(none)"
            set_or_add(metadata, tag, new_value)
            changes.append(f"{tag}: '{old_val}' -> '{new_value}'")

        for isbn in fixes.get("add_isbn", []):
            ident = ET.SubElement(metadata, "{http://purl.org/dc/elements/1.1/}identifier")
            ident.set("{http://www.idpf.org/2007/opf}scheme", "ISBN")
            ident.text = isbn
            changes.append(f"+ISBN: {isbn}")

        if fixes.get("fix_author"):
            author = metadata.find("{http://purl.org/dc/elements/1.1/}creator")
            old_val = author.text
            author.text = fixes["fix_author"]
            changes.append(f"creator: '{old_val}' -> '{fixes['fix_author']}'")

        if not changes:
            print(f"  OK: {os.path.basename(path)}")
            return True

        new_opf = ET.tostring(opf_root, encoding="utf-8", xml_declaration=True)
        with zipfile.ZipFile(tmp_path, 'w', zipfile.ZIP_DEFLATED) as zout:
            for item in zin.infolist():
                zout.writestr(item, new_opf if item.filename == opf_path else zin.read(item.filename))
    os.replace(tmp_path, path)
    print(f"  FIXED {os.path.basename(path)}: {'; '.join(changes)}")
    return True

# === CONFIGURE FIXES BELOW ===
fixes = [
    # Example: fix year + publisher + add ISBN
    # {
    #     "path": f"{BOOKS_DIR}/Author/Book-Author.epub",
    #     "set": {"date": "1942", "publisher": "S. Fischer Verlag"},
    #     "add_isbn": ["978-3-596-29380-3"]
    # },
    # Example: fix inverted author + add ISBN
    # {
    #     "path": f"{BOOKS_DIR}/Author/Book-Author.epub",
    #     "fix_author": "LastName, FirstName",
    #     "add_isbn": ["978-3-8338-7805-3"]
    # },
]

for f in fixes:
    try:
        fix_epub(f["path"], f)
    except Exception as e:
        print(f"  ERROR: {os.path.basename(f['path'])} - {e}")
