"""MEGOSZTOTT (KÖZÖS) DOKUMENTUMOK - segédek (v80). Leírás: docs/KOZOS_DOKUMENTUM.md

MI EZ?
    A Használati utasítás / Gépkönyv / Adatlap NEM egy adott gépre szól, hanem a gép TÍPUSÁRA (gyártó + típus). Ezeket ezért nem kell gépenként újra
    fizikailag feltölteni: egy fájlra több gép hivatkozhat (Miklós kérése).

HOGYAN MŰKÖDIK?
    * A megosztott fájl fizikailag az `uploads/<cég>/kozos/` mappában van (NEM egy gép mappájában), és MINDEN érintett gépnek saját `File` sora van
      ugyanarra az útvonalra (`/uploads/<cég>/kozos/<fájlnév>`). Séma-változás nincs: a "hivatkozás" egyszerűen egy közös útvonalra mutató File-sor,
      ezért minden meglévő olvasó (fájllista, mobil napi ellenőrzés "Használati utasítás letöltése", rendszeradmin export, PDF-ek) változatlanul működik.
    * Az első megosztáskor a fájl a feltöltő gép mappájából a közös mappába költözik (másolás -> az adatbázis commit -> csak utána törlődik a régi példány,
      így egy sikertelen mentés nem veszít fájlt).
    * Törlés: egy hivatkozás törlése csak az adott gép sorát törli; a FIZIKAI fájl csak akkor törlődik, ha az utolsó hivatkozás is megszűnt
      (`arva_kozos_torlese`). A rendszeradmin gép- / céltörlése is ezt használja, így egy gép törlése nem viszi el a többi gép dokumentumát.
    * Kiszolgálás: `/uploads/<cég>/kozos/<fájl>` (routers/files.py) - akkor érhető el, ha a felhasználó LEGALÁBB EGY olyan gépet láthat (is_machine_allowed),
      amelynek van ilyen útvonalú File-sora (a gép-alapú ellenőrzés ide nem alkalmazható, mert a fájl nem egy gép mappájában van).
"""
import os
import shutil
import uuid

import models

# Csak a TÍPUSRA jellemző dokumentumok oszthatók meg; a jegyzőkönyvek, fotók, gépnapló stb. gépenként külön maradnak.
KOZOS_FAJLTIPUSOK = ("Használati utasítás", "Gépkönyv", "Adatlap")
KOZOS_MAPPA_NEV = "kozos"


def kozos_mappa(company_id: int) -> str:
    """A cég közös dokumentum-mappája (fájlrendszer-útvonal, tehát NINCS vezető '/')."""
    return f"uploads/{company_id}/{KOZOS_MAPPA_NEV}"


def kozos_prefix(company_id: int) -> str:
    """Az adatbázisban tárolt útvonal előtagja (vezető '/' -tel)."""
    return f"/{kozos_mappa(company_id)}/"


def kozos_utvonal_e(file_path, company_id: int = None) -> bool:
    """Igaz, ha az útvonal egy közös mappába mutat (adott cégre, vagy - ha company_id None - bármelyik cégére)."""
    if not file_path:
        return False
    if company_id is not None:
        return file_path.startswith(kozos_prefix(company_id))
    reszek = file_path.split("/")          # ['', 'uploads', '<cég>', 'kozos', '<fájl>']
    return len(reszek) == 5 and reszek[1] == "uploads" and reszek[2].isdigit() and reszek[3] == KOZOS_MAPPA_NEV and bool(reszek[4])


def hivatkozas_szam(db, file_path: str) -> int:
    """Hány File-sor mutat erre az útvonalra."""
    return db.query(models.File).filter(models.File.file_path == file_path).count()


def kozosbe_masol(fajl, company_id: int):
    """Biztosítja, hogy a File-sor fizikai fájlja a cég közös mappájában legyen. Ha még nem ott van: átmásolja (a régit NEM törli), és átírja a
    `fajl.file_path`-t. Visszatérés: a régi fizikai fájl útvonala (amit a hívó a COMMIT UTÁN töröl `fizikai_torol`-ral), vagy None, ha már közös
    volt; második elem: az újonnan létrehozott másolat útvonala (hibánál a hívó ezt törli). Hiányzó forrásfájlnál FileNotFoundError."""
    if kozos_utvonal_e(fajl.file_path, company_id):
        return None, None
    forras = (fajl.file_path or "").lstrip("/")
    if not forras or not os.path.isfile(forras):
        raise FileNotFoundError(forras)
    mappa = kozos_mappa(company_id)
    os.makedirs(mappa, exist_ok=True)
    nev = os.path.basename(forras)
    torzs, kiterjesztes = os.path.splitext(nev)
    for kiserlet in range(6):
        jelolt = nev if kiserlet == 0 else f"{torzs}_{uuid.uuid4().hex[:8]}{kiterjesztes}"
        hely = f"{mappa}/{jelolt}"
        try:
            with open(hely, "xb") as cel, open(forras, "rb") as be:       # "xb": soha nem ír felül meglévő fájlt
                shutil.copyfileobj(be, cel)
        except FileExistsError:
            continue
        fajl.file_path = "/" + hely
        return forras, hely
    raise RuntimeError("Nem sikerült egyedi fájlnevet képezni a közös mappában.")


def fizikai_torol(hely) -> None:
    """Fizikai fájl törlése, hiba esetén csendben (a hívó már commitolt; egy árva fájl nem okoz adatvesztést)."""
    if hely:
        try:
            os.remove(hely.lstrip("/"))
        except OSError:
            pass


def kozos_utvonalak(db, gep_idk) -> set:
    """A megadott gépek File-sorainak közös mappába mutató útvonalai (törlés előtt gyűjtendő, törlés után `arva_kozos_torlese`-nek adandó)."""
    if not gep_idk:
        return set()
    return {
        u for (u,) in db.query(models.File.file_path).filter(models.File.machine_id.in_(list(gep_idk))).all()
        if kozos_utvonal_e(u)
    }


def arva_kozos_torlese(db, utvonalak) -> int:
    """A felsorolt közös útvonalak közül azokat a fizikai fájlokat törli, amelyekre MÁR NEM mutat egyetlen File-sor sem. Visszaadja a törölt fájlok számát.
    A hívás a File-sorok törlése UTÁN (azonos vagy külön tranzakcióban, de a törlés láthatóvá válása után) történjen."""
    torolt = 0
    for utvonal in set(utvonalak or []):
        if kozos_utvonal_e(utvonal) and hivatkozas_szam(db, utvonal) == 0:
            if os.path.isfile(utvonal.lstrip("/")):
                fizikai_torol(utvonal)
                torolt += 1
    return torolt
