#!/usr/bin/env python3
"""
Essa versão é de teste
nexo_orphan_scanner.py

A small companion-audit utility for the NEXO File Companion ecosystem.

A NEXO companion is a UTF-8 Markdown file with YAML front matter that sits
beside its asset:

    photo.jpg
    photo.jpg.nexo.md

Over time, drift can appear in a folder tree: an asset with no companion
(never processed, or the companion was deleted by hand), or a companion
with no matching asset (the asset was moved, renamed, or deleted, leaving
a stale ".nexo.md" behind).

This script walks a directory recursively, pairs assets with their
companions by file name, and reports both kinds of drift. It performs no
writes and never touches the asset's bytes or an existing companion's
content -- it only reads file names and, optionally, verifies the
declared SHA-256 against the asset on disk.

This is a companion utility, not part of the reference `nexo` CLI itself;
it is a lightweight example of what a third-party tool built on top of
the NEXO format can look like.
"""

import argparse
import hashlib
import re
import sys
from pathlib import Path

COMPANION_SUFFIX = ".nexo.md"
SHA256_LINE = re.compile(r'^\s*sha256:\s*"?([0-9a-f]{64})"?\s*$', re.MULTILINE)


def compute_sha256(path, chunk_size=1 << 20):
    """Return the lowercase hex SHA-256 digest of a file's bytes."""
    digest = hashlib.sha256()
    with path.open("rb") as handle:
        for chunk in iter(lambda: handle.read(chunk_size), b""):
            digest.update(chunk)
    return digest.hexdigest()


def declared_sha256(companion_path):
    """Extract the `file.sha256` value from a companion, if present."""
    text = companion_path.read_text(encoding="utf-8", errors="replace")
    match = SHA256_LINE.search(text)
    return match.group(1) if match else None


def find_pairs(root):
    """Classify every file under `root` as an asset, a companion, or both."""
    assets, companions = set(), {}
    for path in root.rglob("*"):
        if not path.is_file():
            continue
        if path.name.endswith(COMPANION_SUFFIX):
            asset_name = path.name[: -len(COMPANION_SUFFIX)]
            companions[path.parent / asset_name] = path
        else:
            assets.add(path)
    return assets, companions


def audit(root, verify_hashes):
    """Print a drift report. Return the number of problems found."""
    assets, companions = find_pairs(root)
    orphan_assets = sorted(assets - companions.keys())
    orphan_companions = sorted(companions.keys() - assets)
    paired = sorted(assets & companions.keys())
    problems = len(orphan_assets) + len(orphan_companions)

    print(f"Scanned: {root}")
    print(f"  assets: {len(assets)}  companions: {len(companions)}  paired: {len(paired)}")

    if orphan_assets:
        print("\nAssets without a companion:")
        for asset in orphan_assets:
            print(f"  - {asset}")

    if orphan_companions:
        print("\nCompanions without an asset:")
        for companion in orphan_companions:
            print(f"  - {companion}")

    if verify_hashes:
        print("\nHash verification:")
        for asset in paired:
            declared = declared_sha256(companions[asset])
            if declared is None:
                continue
            actual = compute_sha256(asset)
            if actual != declared:
                problems += 1
                print(f"  MISMATCH  {asset}")
                print(f"    declared: {declared}")
                print(f"    actual:   {actual}")

    if problems == 0:
        print("\nNo drift detected.")
    return problems


def main():
    parser = argparse.ArgumentParser(description="Find orphaned NEXO assets and companions.")
    parser.add_argument("folder", type=Path, help="folder to scan recursively")
    parser.add_argument(
        "--verify-hashes",
        action="store_true",
        help="recompute SHA-256 for paired assets and compare against file.sha256",
    )
    args = parser.parse_args()

    if not args.folder.is_dir():
        print(f"error: not a directory: {args.folder}", file=sys.stderr)
        return 2

    return 1 if audit(args.folder, args.verify_hashes) else 0


if __name__ == "__main__":
    sys.exit(main())
