"""Files that are only in iCloud (macOS "dataless"): check before opening them.

On a Mac with "Optimize Mac Storage", a file in iCloud Drive (Desktop and Documents too) can be only a
placeholder: `ls -lO` shows `compressed,dataless`. ffprobe and ffmpeg then hang for minutes without any
message while macOS tries to fetch it, and an ffmpeg that is killed leaves "moov atom not found". Seen on
29/09/2026 with the BODIES/*.MP4 of ~/Documents.

`garantir_local(paths)` asks macOS to download every such file (`brctl download`), waits a little and, if
something is still in the cloud, raises `SoNaNuvem` with a message in Portuguese that says what to do. On other
systems (or without the flag) it does nothing.

Usage from the command line (the transcribe helpers already call it):
    uv run --directory '<SKILL>' python helpers/icloud.py <file or folder> [...] [--espera 90] [--nao-baixar]
Exit code 0 when everything is on the disk, 1 when something is still only in iCloud.
"""

from __future__ import annotations

import argparse
import os
import shutil
import subprocess
import sys
import time
from pathlib import Path

SF_DATALESS = 0x40000000     # <sys/stat.h>: the file content is not on the disk (iCloud placeholder)
ESPERA_S = 90.0              # how long to wait for iCloud before giving up
PASSO_S = 2.0


class SoNaNuvem(RuntimeError):
    """One or more files are still only in iCloud."""

    def __init__(self, arquivos: list[Path], tentou: bool):
        self.arquivos = arquivos
        super().__init__(mensagem(arquivos, tentou))


def so_na_nuvem(path: Path | str) -> bool:
    """True when the file is an iCloud placeholder (dataless) on macOS."""
    try:
        flags = getattr(os.stat(path), "st_flags", 0) or 0
    except OSError:
        return False
    return bool(flags & SF_DATALESS)


def na_nuvem(paths) -> list[Path]:
    return [Path(p) for p in paths if Path(p).is_file() and so_na_nuvem(p)]


def mensagem(arquivos: list[Path], tentou: bool) -> str:
    nomes = "\n".join(f"  {p}" for p in arquivos[:12]) + (f"\n  (+{len(arquivos) - 12})" if len(arquivos) > 12 else "")
    pedido = ("Pedi ao macOS para baixar e ainda não terminou." if tentou
              else "Nada foi aberto para não travar (o ffmpeg fica parado sem erro nesses arquivos).")
    return (f"{len(arquivos)} arquivo(s) ainda está(ão) só no iCloud, não neste Mac:\n{nomes}\n{pedido}\n"
            "Baixe antes de seguir: no Finder, botão direito no arquivo > Baixar Agora (espere a nuvem sumir do "
            "ícone), ou no Terminal: brctl download '<arquivo>'. Depois rode de novo. Outro jeito: copie os vídeos "
            "para uma pasta fora do iCloud (um disco externo, por exemplo).")


def pedir_download(paths: list[Path]) -> bool:
    brctl = shutil.which("brctl")
    if not brctl:
        return False
    ok = False
    for p in paths:
        try:
            r = subprocess.run([brctl, "download", str(p)], capture_output=True, text=True, timeout=30)
            ok |= r.returncode == 0
        except (OSError, subprocess.SubprocessError):
            pass
    return ok


def garantir_local(paths, baixar: bool = True, espera: float = ESPERA_S, avisar=print) -> None:
    """Raise SoNaNuvem if any file is still only in iCloud after asking for it (at most `espera` seconds)."""
    if sys.platform != "darwin":
        return
    faltam = na_nuvem(paths)
    if not faltam:
        return
    if not baixar:
        raise SoNaNuvem(faltam, tentou=False)
    avisar(f"{len(faltam)} arquivo(s) só no iCloud (ex.: {faltam[0].name}): pedindo para baixar, espero até "
           f"{espera:.0f} s...")
    tentou = pedir_download(faltam)
    if not tentou:
        raise SoNaNuvem(faltam, tentou=False)
    fim = time.monotonic() + espera
    while faltam and time.monotonic() < fim:
        time.sleep(PASSO_S)
        faltam = [p for p in faltam if so_na_nuvem(p)]
    if faltam:
        raise SoNaNuvem(faltam, tentou=True)
    avisar("baixados do iCloud; seguindo.")


def main(argv: list[str] | None = None) -> int:
    ap = argparse.ArgumentParser(description="Confere se os arquivos estão no disco ou só no iCloud (e pede para baixar).")
    ap.add_argument("caminhos", nargs="+", type=Path, help="arquivos ou pastas (sem entrar nas subpastas)")
    ap.add_argument("--espera", type=float, default=ESPERA_S, help=f"segundos esperando o iCloud (padrão {ESPERA_S:g})")
    ap.add_argument("--nao-baixar", action="store_true", help="só confere, não pede para baixar")
    a = ap.parse_args(argv)
    arquivos: list[Path] = []
    for c in a.caminhos:
        arquivos += sorted(p for p in c.iterdir() if p.is_file()) if c.is_dir() else [c]
    try:
        garantir_local(arquivos, baixar=not a.nao_baixar, espera=a.espera)
    except SoNaNuvem as exc:
        print(str(exc), file=sys.stderr)
        return 1
    print(f"{len(arquivos)} arquivo(s) no disco, nenhum só no iCloud.")
    return 0


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