#!/usr/bin/env python3
"""Unpack and format XML contents of Office files (.docx, .pptx, .xlsx)."""

import argparse
import random
import re
import sys
import zipfile
from pathlib import Path

try:
    import defusedxml.minidom as secure_minidom
except ImportError:  # pragma: no cover - fallback for minimal environments
    import xml.dom.minidom as secure_minidom


ENCODING_RE = re.compile(br"encoding=[\"']([^\"']+)[\"']", re.IGNORECASE)


def detect_xml_encoding(raw_bytes: bytes) -> str:
    header = raw_bytes[:256]
    match = ENCODING_RE.search(header)
    if not match:
        return "utf-8"
    value = match.group(1).decode("ascii", errors="ignore").lower()
    if value == "ascii":
        return "ascii"
    return "utf-8"


def _is_within_directory(base_dir: Path, target: Path) -> bool:
    try:
        target.resolve().relative_to(base_dir.resolve())
        return True
    except ValueError:
        return False


def safe_extract_office_archive(input_file: Path, output_dir: Path) -> None:
    with zipfile.ZipFile(input_file, "r") as archive:
        for member in archive.infolist():
            target = output_dir / member.filename
            if not _is_within_directory(output_dir, target):
                raise ValueError(f"Unsafe archive path detected: {member.filename}")
        archive.extractall(output_dir)


def pretty_print_xml_files(output_dir: Path) -> None:
    xml_files = list(output_dir.rglob("*.xml")) + list(output_dir.rglob("*.rels"))
    for xml_file in xml_files:
        raw = xml_file.read_bytes()
        encoding = detect_xml_encoding(raw)
        dom = secure_minidom.parseString(raw)
        xml_file.write_bytes(dom.toprettyxml(indent="  ", encoding=encoding))


def parse_args(argv: list[str]) -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Unpack Office file and pretty-print XML")
    parser.add_argument("office_file", help="Input Office file (.docx/.pptx/.xlsx)")
    parser.add_argument("output_dir", help="Directory where unpacked files are written")
    return parser.parse_args(argv)


def main(argv: list[str] | None = None) -> int:
    args = parse_args(argv or sys.argv[1:])
    input_path = Path(args.office_file)
    output_path = Path(args.output_dir)

    if not input_path.exists():
        print(f"Error: file not found: {input_path}", file=sys.stderr)
        return 1
    if input_path.suffix.lower() not in {".docx", ".pptx", ".xlsx"}:
        print("Error: office_file must be a .docx, .pptx, or .xlsx file", file=sys.stderr)
        return 1

    output_path.mkdir(parents=True, exist_ok=True)

    try:
        safe_extract_office_archive(input_path, output_path)
        pretty_print_xml_files(output_path)
    except zipfile.BadZipFile:
        print(f"Error: invalid Office archive: {input_path}", file=sys.stderr)
        return 1
    except Exception as exc:
        print(f"Error: {exc}", file=sys.stderr)
        return 1

    if input_path.suffix.lower() == ".docx":
        suggested_rsid = "".join(random.choices("0123456789ABCDEF", k=8))
        print(f"Suggested RSID for edit session: {suggested_rsid}")

    return 0


if __name__ == "__main__":
    raise SystemExit(main())
