import io
import json
import os
from datetime import date, datetime

from fastapi import APIRouter, Request, Depends, Form, UploadFile, File, HTTPException, status
from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse, StreamingResponse, FileResponse
from sqlalchemy import or_
from sqlalchemy.orm import Session, joinedload

import models
import aktivalas_utils
from database import get_db
from security import (
    get_current_user, get_working_company_id, is_company_active,
    irasi_tiltas_oka, IRASI_TILTAS_UZENETEK,
    require_modul, get_allowed_companies_list, safe_json_for_script,
    is_safe_extension, sanitize_filename, get_password_hash, van_e_tobb_modulja,
    sugo_engedelyezve, is_company_allowed, jelszo_eleg_eros_e,
)
from image_utils import tomorit_kepet
from hibakodok import hiba_uzenet
from vivu_pdf_utils import VivuNapiOsszefoglaloPDF, VivuEllenorzesPDF, SULYOSSAG_CIMKE
from templates_env import templates

router = APIRouter(dependencies=[Depends(require_modul("VIVU"))])

SULYOSSAGOK = ["ENYHE", "KOZEPES", "SULYOS"]
TIPUSOK = ["BEJARAS", "DOKUMENTACIO"]
SZAKTERULET_MEZOK = [
    ("szakterulet_iparbiztonsagi", "Iparbiztonsági"),
    ("szakterulet_tuzvedelmi", "Tűzvédelmi"),
    ("szakterulet_munkavedelmi", "Munkavédelmi"),
    ("szakterulet_kornyezetvedelmi", "Környezetvédelmi"),
]

ADMIN_SZEREPKOROK = ["Admin"]
FENYKEP_FELTOLTHET_SZEREPKOROK = ["Admin", "VIVU"]


def _lehet_adminisztralni(current_user, working_company_id, db) -> bool:
    """Teljes beállítási jog: katalógusok, Megrendelő/telephely létrehozása/szerkesztése,
    hozzárendelések - kizárólag Admin, a VIVU ügyintéző sem fér hozzá."""
    if current_user.role not in ADMIN_SZEREPKOROK:
        return False
    if not is_company_active(working_company_id, db):
        return False
    if current_user.csak_olvasas:
        return False
    return True


def _lehet_ellenorizni(current_user, working_company_id, db) -> bool:
    """Bejárás/dokumentáció rögzítésének joga: Admin, vagy a cég (legfeljebb
    egy) VIVU ügyintézője - nincs Megrendelő-szintű hozzárendelés-ellenőrzés, mert cégenként
    úgyis csak egy ügyintéző lehet, ő automatikusan hozzáfér a cég összes Megrendelőjéhez."""
    if not is_company_active(working_company_id, db):
        return False
    if current_user.csak_olvasas:
        return False
    if current_user.role in ADMIN_SZEREPKOROK:
        return True
    if current_user.role == "VIVU":
        return current_user.company_id == working_company_id
    return False


def _ugyfel_lathato_e(current_user, ugyfel) -> bool:
    """Megtekintő csak a hozzá rendelt Megrendelő(ke)t láthatja; a cég VIVU ügyintézője az
    összes Megrendelőt; Admin mindent."""
    if current_user.role in ADMIN_SZEREPKOROK:
        return True
    if current_user.role == "Megtekintő":
        return ugyfel in current_user.vivu_ugyfelek
    if current_user.role == "VIVU":
        return current_user.company_id == ugyfel.company_id
    return False


def _lathato_ugyfelek(current_user, working_company_id, db) -> list:
    """A jelenlegi felhasználó számára látható Megrendelők listája - Megtekintő csak a
    sajátjait, Admin/VIVU a cég összes Megrendelőjét látja.
    J-10 (v73): MINDEN MÁS szerepkör (Felhasználó, ügyintéző, szakértő, vizsgáló...) üres listát kap - ugyanaz a szabály,
    mint a részletoldalaknál (_ugyfel_lathato_e). Korábban ők a munkakörnyezet összes Megrendelőjét és statisztikáját látták."""
    if current_user.role == "Megtekintő":
        return list(current_user.vivu_ugyfelek)
    if current_user.role in ADMIN_SZEREPKOROK or current_user.role == "VIVU":
        return db.query(models.VivuUgyfel).filter(models.VivuUgyfel.company_id == working_company_id).all()
    return []


def _ceg_kontextus(request, current_user, db):
    """Munkakörnyezet-context egységesen - lásd routers/ebk.py azonos nevű függvényének
    kommentjét a "beragadt cégválasztó" jelenség magyarázatáért."""
    working_company_id = get_working_company_id(current_user, request)
    cegek = get_allowed_companies_list(current_user, db, "VIVU")
    aktualis_ceg_nev_ha_eltero = None
    if working_company_id not in {c.id for c in cegek}:
        aktualis = db.query(models.Company).filter(models.Company.id == working_company_id).first()
        aktualis_ceg_nev_ha_eltero = aktualis.nev if aktualis else f"#{working_company_id}"
    sugo_e = sugo_engedelyezve(working_company_id, db)
    return working_company_id, cegek, aktualis_ceg_nev_ha_eltero, sugo_e


def _parse_date(ertek: str):
    if not ertek:
        return None
    try:
        return datetime.strptime(ertek, "%Y-%m-%d").date()
    except ValueError:
        return None


def _celellenorzes_opciok(tetelek) -> list:
    """Lásd routers/ebk.py azonos nevű függvényét - ugyanaz a "Kategória – részletek"
    konvenció adja a célellenőrzés legördülő opcióit, itt egy sima (nem projekt-szűrt)
    ellenőrzési tétel-listából."""
    kategoriak = []
    for tetel in tetelek:
        szoveg = tetel.szoveg or ""
        kategoria = szoveg.split(" – ", 1)[0].strip()
        if kategoria and kategoria not in kategoriak:
            kategoriak.append(kategoria)
    return sorted(kategoriak)


def _epuletek_json_ugyfelhez(ugyfel, db: Session) -> str:
    """Az adott Megrendelő telephelyeinek épület+szint fáját szolgáltatja JSON-ban a
    helyszín-választó kaszkádolt legördülőkhöz (telephely -> épület -> szint)."""
    telephely_idk = [t.id for t in ugyfel.telephelyek if t.aktiv]
    if not telephely_idk:
        return safe_json_for_script([])

    epuletek = db.query(models.VivuEpulet).filter(
        models.VivuEpulet.telephely_id.in_(telephely_idk), models.VivuEpulet.aktiv == True
    ).all()
    szintek = db.query(models.VivuSzint).filter(
        models.VivuSzint.epulet_id.in_([e.id for e in epuletek]), models.VivuSzint.aktiv == True
    ).all() if epuletek else []

    szintek_epulet_szerint = {}
    for sz in szintek:
        szintek_epulet_szerint.setdefault(sz.epulet_id, []).append({"id": sz.id, "nev": sz.nev})

    return safe_json_for_script([
        {"id": e.id, "telephely_id": e.telephely_id, "nev": e.nev, "szintek": szintek_epulet_szerint.get(e.id, [])}
        for e in epuletek
    ])


# --- DASHBOARD (kezdőlap) ---

@router.get("/vivu/dashboard", response_class=HTMLResponse)
def vivu_dashboard(
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    """A modul kezdőlapja - ez az egyetlen VIVÜ-oldal, amin a modulváltó ikon megjelenik,
    innen navigál tovább a felhasználó a Megrendelő-listára/beállításokra."""
    working_company_id, cegek, aktualis_ceg_nev_ha_eltero, sugo_e = _ceg_kontextus(request, current_user, db)
    ugyfelek = _lathato_ugyfelek(current_user, working_company_id, db)
    ugyfel_idk = [u.id for u in ugyfelek]

    ellenorzesek = db.query(models.VivuEllenorzes).join(models.VivuTelephely).options(
        joinedload(models.VivuEllenorzes.telephely)
    ).filter(
        models.VivuTelephely.ugyfel_id.in_(ugyfel_idk)
    ).all() if ugyfel_idk else []
    ellenorzes_idk = [e.id for e in ellenorzesek]

    mai_nap = date.today()
    ellenorzesek_e_honap = sum(1 for e in ellenorzesek if e.datum and e.datum.year == mai_nap.year and e.datum.month == mai_nap.month)

    szabalytalansagok = db.query(models.VivuEllenorzesTetel).options(joinedload(models.VivuEllenorzesTetel.tetel)).filter(
        models.VivuEllenorzesTetel.ellenorzes_id.in_(ellenorzes_idk),
        models.VivuEllenorzesTetel.statusz == "SZABALYTALANSAG",
    ).all() if ellenorzes_idk else []

    sulyossag_szamlalo = {"ENYHE": 0, "KOZEPES": 0, "SULYOS": 0}
    for sz in szabalytalansagok:
        if sz.sulyossag in sulyossag_szamlalo:
            sulyossag_szamlalo[sz.sulyossag] += 1

    jelleg_szamlalo = {}
    for sz in szabalytalansagok:
        szoveg = sz.tetel.szoveg if sz.tetel else "Ismeretlen tétel"
        jelleg_szamlalo[szoveg] = jelleg_szamlalo.get(szoveg, 0) + 1
    leggyakoribb_jellegek = sorted(jelleg_szamlalo.items(), key=lambda x: x[1], reverse=True)[:5]

    legutobbi_ellenorzesek = sorted(ellenorzesek, key=lambda e: e.letrehozva, reverse=True)[:10]

    return templates.TemplateResponse(
        request=request,
        name="vivu_dashboard.html",
        context={
            "user": current_user,
            "cegek": cegek,
            "aktualis_ceg_nev_ha_eltero": aktualis_ceg_nev_ha_eltero,
            "sugo_e": sugo_e,
            "working_company_id": working_company_id,
            "megrendelok_szama": len(ugyfelek),
            "ellenorzesek_szama": len(ellenorzesek),
            "ellenorzesek_e_honap": ellenorzesek_e_honap,
            "szabalytalansagok_szama": len(szabalytalansagok),
            "sulyossag_szamlalo": sulyossag_szamlalo,
            "leggyakoribb_jellegek": leggyakoribb_jellegek,
            "legutobbi_ellenorzesek": legutobbi_ellenorzesek,
            "tobb_modul_van": van_e_tobb_modulja(current_user, db),
        }
    )


@router.get("/vivu/hasznalati-utmutato", response_class=HTMLResponse)
def vivu_hasznalati_utmutato(
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    """A VIVÜ modul írott használati útmutatója - cégenként, a rendszeradmin által
    ki/bekapcsolható (lásd security.sugo_engedelyezve), ha a cég nem kéri, az oldal
    egyáltalán nem érhető el."""
    working_company_id = get_working_company_id(current_user, request)
    if not sugo_engedelyezve(working_company_id, db):
        return RedirectResponse(url="/vivu", status_code=303)

    return templates.TemplateResponse(
        request=request,
        name="vivu_hasznalati_utmutato.html",
        context={
            "user": current_user,
            "working_company_id": working_company_id,
            "tobb_modul_van": van_e_tobb_modulja(current_user, db),
        }
    )


@router.get("/uploads/{company_id}/vivu/{ellenorzes_id}/{filename}")
def vivu_fajl_kiszolgalas(
    company_id: int,
    ellenorzes_id: int,
    filename: str,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    """A VIVÜ szabálytalansági fényképek hitelesített kiszolgálása - lásd routers/ebk.py
    azonos nevű route-jának kommentjét arról, miért nem szabad ezt statikusan kiszolgálni."""
    ellenorzes = db.query(models.VivuEllenorzes).join(models.VivuTelephely).join(models.VivuUgyfel).filter(
        models.VivuEllenorzes.id == ellenorzes_id,
        models.VivuUgyfel.company_id == company_id,
    ).first()

    if not ellenorzes or not is_company_allowed(current_user, company_id) or not _ugyfel_lathato_e(current_user, ellenorzes.telephely.ugyfel):
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Nincs jogosultságod a kért dokumentum eléréséhez!")

    safe_filename = sanitize_filename(filename)
    file_path = f"uploads/{company_id}/vivu/{ellenorzes_id}/{safe_filename}"

    if not os.path.exists(file_path):
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="A keresett fájl nem található a szerveren.")

    return FileResponse(file_path)


# --- MEGRENDELŐK (a modul fő belépési pontja - nincs többé köztes "Projekt" fogalom) ---

@router.get("/vivu", response_class=HTMLResponse)
def vivu_ugyfelek_lista(
    request: Request,
    kereses: str = "",
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id, cegek, aktualis_ceg_nev_ha_eltero, sugo_e = _ceg_kontextus(request, current_user, db)

    if current_user.role == "Megtekintő":
        ugyfelek = sorted(current_user.vivu_ugyfelek, key=lambda u: u.nev or "")
        if kereses:
            term = kereses.lower()
            ugyfelek = [u for u in ugyfelek if term in (u.nev or "").lower()]
    elif current_user.role not in ADMIN_SZEREPKOROK and current_user.role != "VIVU":
        # J-10 (v73): a többi szerepkörnek (Felhasználó, ügyintéző, szakértő, vizsgáló) nincs hozzárendelt Megrendelője
        # (a részletoldalak is tiltottak számukra), ezért a lista üres - korábban a cég összes Megrendelőjét látták.
        ugyfelek = []
    else:
        query = db.query(models.VivuUgyfel).filter(
            models.VivuUgyfel.company_id == working_company_id, models.VivuUgyfel.aktiv == True,
        )
        if kereses:
            term = f"%{kereses}%"
            query = query.filter(models.VivuUgyfel.nev.ilike(term))
        ugyfelek = query.order_by(models.VivuUgyfel.nev).all()

    return templates.TemplateResponse(
        request=request,
        name="vivu_ugyfelek.html",
        context={
            "user": current_user,
            "cegek": cegek,
            "aktualis_ceg_nev_ha_eltero": aktualis_ceg_nev_ha_eltero,
            "sugo_e": sugo_e,
            "working_company_id": working_company_id,
            "ugyfelek": ugyfelek,
            "kereses": kereses,
            "szerkesztheto": _lehet_adminisztralni(current_user, working_company_id, db),
        }
    )


@router.post("/vivu/uj-ugyfel")
def uj_ugyfel_mentese(
    request: Request,
    nev: str = Form(...),
    cim: str = Form(""),
    kapcsolattarto_nev: str = Form(""),
    kapcsolattarto_elerhetoseg: str = Form(""),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id = get_working_company_id(current_user, request)
    if not _lehet_adminisztralni(current_user, working_company_id, db):
        return RedirectResponse(url="/vivu", status_code=303)

    db.add(models.VivuUgyfel(
        company_id=working_company_id, nev=nev, cim=cim or None,
        kapcsolattarto_nev=kapcsolattarto_nev or None, kapcsolattarto_elerhetoseg=kapcsolattarto_elerhetoseg or None,
    ))
    db.commit()
    return RedirectResponse(url="/vivu", status_code=303)


@router.delete("/vivu/ugyfel/{ugyfel_id}")
def ugyfel_torlese(
    ugyfel_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    if current_user.role not in ADMIN_SZEREPKOROK:
        return JSONResponse(status_code=403, content={"message": "Csak Admin!"})

    working_company_id = get_working_company_id(current_user, request)

    if not is_company_active(working_company_id, db):
        return JSONResponse(status_code=403, content={"message": "Ez a cég inaktív státuszú, törlés nem lehetséges!"})

    if current_user.csak_olvasas:
        return JSONResponse(status_code=403, content={"message": "Csak megtekintési jogosultsággal rendelkezel, szerkesztés nem lehetséges!"})
    ugyfel = db.query(models.VivuUgyfel).filter(
        models.VivuUgyfel.id == ugyfel_id, models.VivuUgyfel.company_id == working_company_id,
    ).first()
    if ugyfel:
        ugyfel.aktiv = False
        db.commit()
    return JSONResponse(status_code=200, content={"message": "Törölve."})


@router.get("/vivu/ugyfel/{ugyfel_id}", response_class=HTMLResponse)
def ugyfel_adatlap(
    ugyfel_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id, cegek, aktualis_ceg_nev_ha_eltero, sugo_e = _ceg_kontextus(request, current_user, db)
    ugyfel = db.query(models.VivuUgyfel).filter(
        models.VivuUgyfel.id == ugyfel_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if not ugyfel:
        return RedirectResponse(url="/vivu", status_code=303)
    if not _ugyfel_lathato_e(current_user, ugyfel):
        return RedirectResponse(url="/vivu", status_code=303)

    telephelyek = [t for t in ugyfel.telephelyek if t.aktiv]

    ellenorzesek = db.query(models.VivuEllenorzes).join(models.VivuTelephely).filter(
        models.VivuTelephely.ugyfel_id == ugyfel_id
    ).order_by(models.VivuEllenorzes.letrehozva.desc()).all()

    van_e_ellenorizheto_tetel = db.query(models.VivuEllenorzesiTetel).filter(
        or_(models.VivuEllenorzesiTetel.company_id == None, models.VivuEllenorzesiTetel.company_id == working_company_id),
        models.VivuEllenorzesiTetel.aktiv == True,
    ).count() > 0

    lehet_adminisztralni = _lehet_adminisztralni(current_user, working_company_id, db)

    hozzarendelheto_megtekintok = []
    if lehet_adminisztralni:
        mar_hozzarendelt_idk = {u.id for u in ugyfel.megrendelo_felhasznalok}
        hozzarendelheto_megtekintok = [
            u for u in db.query(models.User).filter(
                models.User.company_id == working_company_id, models.User.role == "Megtekintő"
            ).all() if u.id not in mar_hozzarendelt_idk
        ]

    epuletek_telephely_szerint = {}
    szintek_epulet_szerint = {}
    if telephelyek:
        epuletek = db.query(models.VivuEpulet).filter(
            models.VivuEpulet.telephely_id.in_([t.id for t in telephelyek]), models.VivuEpulet.aktiv == True
        ).all()
        for e in epuletek:
            epuletek_telephely_szerint.setdefault(e.telephely_id, []).append(e)

        if epuletek:
            szintek = db.query(models.VivuSzint).filter(
                models.VivuSzint.epulet_id.in_([e.id for e in epuletek]), models.VivuSzint.aktiv == True
            ).all()
            for sz in szintek:
                szintek_epulet_szerint.setdefault(sz.epulet_id, []).append(sz)

    return templates.TemplateResponse(
        request=request,
        name="vivu_ugyfel_adatlap.html",
        context={
            "user": current_user,
            "cegek": cegek,
            "aktualis_ceg_nev_ha_eltero": aktualis_ceg_nev_ha_eltero,
            "sugo_e": sugo_e,
            "working_company_id": working_company_id,
            "ugyfel": ugyfel,
            "telephelyek": telephelyek,
            "epuletek_telephely_szerint": epuletek_telephely_szerint,
            "szintek_epulet_szerint": szintek_epulet_szerint,
            "hozzarendelheto_megtekintok": hozzarendelheto_megtekintok,
            "ellenorzesek": ellenorzesek,
            "indithato_ellenorzes": bool(telephelyek) and van_e_ellenorizheto_tetel,
            "lehet_adminisztralni": lehet_adminisztralni,
            "lehet_ellenorizni": _lehet_ellenorizni(current_user, working_company_id, db),
            "szerkesztheto": lehet_adminisztralni,
            "mai_nap": date.today().isoformat(),
        }
    )


@router.post("/vivu/ugyfel/{ugyfel_id}/szerkesztes")
def ugyfel_szerkesztese(
    ugyfel_id: int,
    request: Request,
    nev: str = Form(...),
    cim: str = Form(""),
    kapcsolattarto_nev: str = Form(""),
    kapcsolattarto_elerhetoseg: str = Form(""),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id = get_working_company_id(current_user, request)
    if not _lehet_adminisztralni(current_user, working_company_id, db):
        return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}", status_code=303)

    ugyfel = db.query(models.VivuUgyfel).filter(
        models.VivuUgyfel.id == ugyfel_id, models.VivuUgyfel.company_id == working_company_id,
    ).first()
    if ugyfel:
        ugyfel.nev = nev
        ugyfel.cim = cim or None
        ugyfel.kapcsolattarto_nev = kapcsolattarto_nev or None
        ugyfel.kapcsolattarto_elerhetoseg = kapcsolattarto_elerhetoseg or None
        db.commit()
    return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}", status_code=303)


# --- MEGRENDELŐI HOZZÁFÉRÉS (korlátozott, csak-olvasás fiók egy adott Megrendelőhöz) ---

@router.post("/vivu/ugyfel/{ugyfel_id}/megrendelo-uj-felhasznalo")
def megrendelo_uj_felhasznalo(
    ugyfel_id: int,
    request: Request,
    username: str = Form(...),
    email: str = Form(...),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    if current_user.role not in ADMIN_SZEREPKOROK:
        return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}", status_code=303)

    working_company_id = get_working_company_id(current_user, request)

    if not is_company_active(working_company_id, db):
        return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}?hiba=inaktiv_ceg", status_code=303)

    if current_user.csak_olvasas:
        return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}?hiba=csak_olvasas", status_code=303)

    ugyfel = db.query(models.VivuUgyfel).filter(
        models.VivuUgyfel.id == ugyfel_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if not ugyfel:
        return RedirectResponse(url="/vivu", status_code=303)

    if db.query(models.User).filter(models.User.email == email).first():
        return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}?hiba=email_foglalt", status_code=303)

    if not aktivalas_utils.email_ervenyes_e(email):
        return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}?hiba=email_ervenytelen", status_code=303)

    megrendelo_user = models.User(
        username=username, email=email, hashed_password=aktivalas_utils.jelszo_nelkuli_hash(),
        role="Megtekintő", company_id=working_company_id, hozzaferes_szint="CEG", csak_olvasas=True,
    )
    vivu_modul = db.query(models.Modul).filter(models.Modul.kod == "VIVU").first()
    if vivu_modul:
        megrendelo_user.sajat_modulok.append(vivu_modul)

    db.add(megrendelo_user)
    aktivalas_utils.meghivas(db, megrendelo_user, current_user)   # aktiváló levél (E szelet)
    db.commit()
    db.refresh(megrendelo_user)

    ugyfel.megrendelo_felhasznalok.append(megrendelo_user)
    db.commit()
    return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}", status_code=303)


@router.post("/vivu/ugyfel/{ugyfel_id}/megrendelo-hozzarendeles")
def megrendelo_hozzarendeles(
    ugyfel_id: int,
    request: Request,
    user_id: int = Form(...),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    if current_user.role not in ADMIN_SZEREPKOROK:
        return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}", status_code=303)

    working_company_id = get_working_company_id(current_user, request)

    if not is_company_active(working_company_id, db):
        return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}?hiba=inaktiv_ceg", status_code=303)

    if current_user.csak_olvasas:
        return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}?hiba=csak_olvasas", status_code=303)

    ugyfel = db.query(models.VivuUgyfel).filter(
        models.VivuUgyfel.id == ugyfel_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    megrendelo_user = db.query(models.User).filter(
        models.User.id == user_id, models.User.company_id == working_company_id, models.User.role == "Megtekintő"
    ).first()
    if not ugyfel or not megrendelo_user:
        return RedirectResponse(url="/vivu", status_code=303)

    if megrendelo_user not in ugyfel.megrendelo_felhasznalok:
        ugyfel.megrendelo_felhasznalok.append(megrendelo_user)
        db.commit()
    return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}", status_code=303)


@router.delete("/vivu/ugyfel/{ugyfel_id}/megrendelo/{user_id}")
def megrendelo_eltavolitasa(
    ugyfel_id: int,
    user_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    if current_user.role not in ADMIN_SZEREPKOROK:
        return JSONResponse(status_code=403, content={"message": "Csak Admin!"})

    working_company_id = get_working_company_id(current_user, request)

    if not is_company_active(working_company_id, db):
        return JSONResponse(status_code=403, content={"message": "Ez a cég inaktív státuszú, törlés nem lehetséges!"})

    if current_user.csak_olvasas:
        return JSONResponse(status_code=403, content={"message": "Csak megtekintési jogosultsággal rendelkezel, szerkesztés nem lehetséges!"})
    ugyfel = db.query(models.VivuUgyfel).filter(
        models.VivuUgyfel.id == ugyfel_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    megrendelo_user = db.query(models.User).filter(models.User.id == user_id).first()
    if ugyfel and megrendelo_user and megrendelo_user in ugyfel.megrendelo_felhasznalok:
        ugyfel.megrendelo_felhasznalok.remove(megrendelo_user)
        db.commit()
    return JSONResponse(status_code=200, content={"message": "Hozzáférés eltávolítva."})


# --- TELEPHELYEK / ÉPÜLETEK / SZINTEK (mindig egy adott Megrendelő alatt) ---

@router.post("/vivu/ugyfel/{ugyfel_id}/uj-telephely")
def uj_telephely_mentese(
    ugyfel_id: int,
    request: Request,
    nev: str = Form(...),
    cim: str = Form(""),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id = get_working_company_id(current_user, request)
    ugyfel = db.query(models.VivuUgyfel).filter(
        models.VivuUgyfel.id == ugyfel_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if not ugyfel:
        return RedirectResponse(url="/vivu", status_code=303)
    if not _lehet_adminisztralni(current_user, working_company_id, db):
        return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}", status_code=303)

    db.add(models.VivuTelephely(ugyfel_id=ugyfel_id, nev=nev, cim=cim or None))
    db.commit()
    return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}", status_code=303)


@router.post("/vivu/telephely/{telephely_id}/szerkesztes")
def telephely_szerkesztese(
    telephely_id: int,
    request: Request,
    nev: str = Form(...),
    cim: str = Form(""),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id = get_working_company_id(current_user, request)
    telephely = db.query(models.VivuTelephely).join(models.VivuUgyfel).filter(
        models.VivuTelephely.id == telephely_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if not telephely:
        return RedirectResponse(url="/vivu", status_code=303)
    if not _lehet_adminisztralni(current_user, working_company_id, db):
        return RedirectResponse(url=f"/vivu/ugyfel/{telephely.ugyfel_id}", status_code=303)

    telephely.nev = nev
    telephely.cim = cim or None
    db.commit()
    return RedirectResponse(url=f"/vivu/ugyfel/{telephely.ugyfel_id}", status_code=303)


@router.delete("/vivu/telephely/{telephely_id}")
def telephely_torlese(
    telephely_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    if current_user.role not in ADMIN_SZEREPKOROK:
        return JSONResponse(status_code=403, content={"message": "Csak Admin!"})

    working_company_id = get_working_company_id(current_user, request)

    if not is_company_active(working_company_id, db):
        return JSONResponse(status_code=403, content={"message": "Ez a cég inaktív státuszú, törlés nem lehetséges!"})

    if current_user.csak_olvasas:
        return JSONResponse(status_code=403, content={"message": "Csak megtekintési jogosultsággal rendelkezel, szerkesztés nem lehetséges!"})
    telephely = db.query(models.VivuTelephely).join(models.VivuUgyfel).filter(
        models.VivuTelephely.id == telephely_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if telephely:
        telephely.aktiv = False
        db.commit()
    return JSONResponse(status_code=200, content={"message": "Törölve."})


@router.post("/vivu/uj-epulet")
def uj_epulet_mentese(
    request: Request,
    telephely_id: int = Form(...),
    nev: str = Form(...),
    leiras: str = Form(""),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id = get_working_company_id(current_user, request)
    telephely = db.query(models.VivuTelephely).join(models.VivuUgyfel).filter(
        models.VivuTelephely.id == telephely_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if not telephely:
        return RedirectResponse(url="/vivu", status_code=303)
    if not _lehet_adminisztralni(current_user, working_company_id, db):
        return RedirectResponse(url=f"/vivu/ugyfel/{telephely.ugyfel_id}", status_code=303)

    db.add(models.VivuEpulet(telephely_id=telephely_id, nev=nev, leiras=leiras or None))
    db.commit()
    return RedirectResponse(url=f"/vivu/ugyfel/{telephely.ugyfel_id}", status_code=303)


@router.post("/vivu/epulet/{epulet_id}/szerkesztes")
def epulet_szerkesztese(
    epulet_id: int,
    request: Request,
    nev: str = Form(...),
    leiras: str = Form(""),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id = get_working_company_id(current_user, request)
    epulet = db.query(models.VivuEpulet).join(models.VivuTelephely).join(models.VivuUgyfel).filter(
        models.VivuEpulet.id == epulet_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if not epulet:
        return RedirectResponse(url="/vivu", status_code=303)
    if not _lehet_adminisztralni(current_user, working_company_id, db):
        return RedirectResponse(url=f"/vivu/ugyfel/{epulet.telephely.ugyfel_id}", status_code=303)

    epulet.nev = nev
    epulet.leiras = leiras or None
    db.commit()
    return RedirectResponse(url=f"/vivu/ugyfel/{epulet.telephely.ugyfel_id}", status_code=303)


@router.delete("/vivu/epulet/{epulet_id}")
def epulet_torlese(
    epulet_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    if current_user.role not in ADMIN_SZEREPKOROK:
        return JSONResponse(status_code=403, content={"message": "Csak Admin!"})

    working_company_id = get_working_company_id(current_user, request)

    if not is_company_active(working_company_id, db):
        return JSONResponse(status_code=403, content={"message": "Ez a cég inaktív státuszú, törlés nem lehetséges!"})

    if current_user.csak_olvasas:
        return JSONResponse(status_code=403, content={"message": "Csak megtekintési jogosultsággal rendelkezel, szerkesztés nem lehetséges!"})
    epulet = db.query(models.VivuEpulet).join(models.VivuTelephely).join(models.VivuUgyfel).filter(
        models.VivuEpulet.id == epulet_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if epulet:
        epulet.aktiv = False
        db.commit()
    return JSONResponse(status_code=200, content={"message": "Törölve."})


@router.post("/vivu/uj-szint")
def uj_szint_mentese(
    request: Request,
    epulet_id: int = Form(...),
    nev: str = Form(...),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id = get_working_company_id(current_user, request)
    epulet = db.query(models.VivuEpulet).join(models.VivuTelephely).join(models.VivuUgyfel).filter(
        models.VivuEpulet.id == epulet_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if not epulet:
        return RedirectResponse(url="/vivu", status_code=303)
    if not _lehet_adminisztralni(current_user, working_company_id, db):
        return RedirectResponse(url=f"/vivu/ugyfel/{epulet.telephely.ugyfel_id}", status_code=303)

    db.add(models.VivuSzint(epulet_id=epulet_id, nev=nev))
    db.commit()
    return RedirectResponse(url=f"/vivu/ugyfel/{epulet.telephely.ugyfel_id}", status_code=303)


@router.delete("/vivu/szint/{szint_id}")
def szint_torlese(
    szint_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    if current_user.role not in ADMIN_SZEREPKOROK:
        return JSONResponse(status_code=403, content={"message": "Csak Admin!"})

    working_company_id = get_working_company_id(current_user, request)

    if not is_company_active(working_company_id, db):
        return JSONResponse(status_code=403, content={"message": "Ez a cég inaktív státuszú, törlés nem lehetséges!"})

    if current_user.csak_olvasas:
        return JSONResponse(status_code=403, content={"message": "Csak megtekintési jogosultsággal rendelkezel, szerkesztés nem lehetséges!"})
    szint = db.query(models.VivuSzint).join(models.VivuEpulet).join(models.VivuTelephely).join(models.VivuUgyfel).filter(
        models.VivuSzint.id == szint_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if szint:
        szint.aktiv = False
        db.commit()
    return JSONResponse(status_code=200, content={"message": "Törölve."})


# --- ELLENŐRZÉSI TÉTELEK (céges sablon-katalógus - globális, nincs per-Megrendelő toggle) ---

@router.get("/vivu/ellenorzesi-tetelek", response_class=HTMLResponse)
def vivu_tetelek_lista(
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    if current_user.role not in ADMIN_SZEREPKOROK:
        return RedirectResponse(url="/vivu", status_code=303)

    working_company_id, cegek, aktualis_ceg_nev_ha_eltero, sugo_e = _ceg_kontextus(request, current_user, db)
    tetelek = db.query(models.VivuEllenorzesiTetel).filter(
        or_(models.VivuEllenorzesiTetel.company_id == None, models.VivuEllenorzesiTetel.company_id == working_company_id),
        models.VivuEllenorzesiTetel.aktiv == True,
    ).order_by(models.VivuEllenorzesiTetel.tipus, models.VivuEllenorzesiTetel.szoveg).all()

    return templates.TemplateResponse(
        request=request,
        name="vivu_tetelek.html",
        context={
            "user": current_user,
            "cegek": cegek,
            "aktualis_ceg_nev_ha_eltero": aktualis_ceg_nev_ha_eltero,
            "sugo_e": sugo_e,
            "working_company_id": working_company_id,
            "tetelek": tetelek,
            "szerkesztheto": _lehet_adminisztralni(current_user, working_company_id, db),
        }
    )


@router.post("/vivu/uj-tetel")
def uj_tetel_mentese(
    request: Request,
    szoveg: str = Form(...),
    kotelezo: str = Form(""),
    tipus: str = Form("BEJARAS"),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id = get_working_company_id(current_user, request)
    if not _lehet_adminisztralni(current_user, working_company_id, db):
        return RedirectResponse(url="/vivu/ellenorzesi-tetelek", status_code=303)

    db.add(models.VivuEllenorzesiTetel(
        company_id=working_company_id, szoveg=szoveg,
        kotelezo=(kotelezo == "on"), tipus=tipus if tipus in TIPUSOK else "BEJARAS",
    ))
    db.commit()
    return RedirectResponse(url="/vivu/ellenorzesi-tetelek", status_code=303)


@router.post("/vivu/tetel/{tetel_id}/szerkesztes")
def tetel_szerkesztese(
    tetel_id: int,
    request: Request,
    szoveg: str = Form(...),
    kotelezo: str = Form(""),
    tipus: str = Form("BEJARAS"),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id = get_working_company_id(current_user, request)
    if not _lehet_adminisztralni(current_user, working_company_id, db):
        return RedirectResponse(url="/vivu/ellenorzesi-tetelek", status_code=303)

    tetel = db.query(models.VivuEllenorzesiTetel).filter(
        models.VivuEllenorzesiTetel.id == tetel_id, models.VivuEllenorzesiTetel.company_id == working_company_id
    ).first()
    if tetel:
        tetel.szoveg = szoveg
        tetel.kotelezo = (kotelezo == "on")
        tetel.tipus = tipus if tipus in TIPUSOK else "BEJARAS"
        db.commit()
    return RedirectResponse(url="/vivu/ellenorzesi-tetelek", status_code=303)


@router.delete("/vivu/tetel/{tetel_id}")
def tetel_torlese(
    tetel_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    if current_user.role not in ADMIN_SZEREPKOROK:
        return JSONResponse(status_code=403, content={"message": "Csak Admin!"})

    working_company_id = get_working_company_id(current_user, request)

    if not is_company_active(working_company_id, db):
        return JSONResponse(status_code=403, content={"message": "Ez a cég inaktív státuszú, törlés nem lehetséges!"})

    if current_user.csak_olvasas:
        return JSONResponse(status_code=403, content={"message": "Csak megtekintési jogosultsággal rendelkezel, szerkesztés nem lehetséges!"})
    tetel = db.query(models.VivuEllenorzesiTetel).filter(
        models.VivuEllenorzesiTetel.id == tetel_id, models.VivuEllenorzesiTetel.company_id == working_company_id
    ).first()
    if tetel:
        tetel.aktiv = False
        db.commit()
    return JSONResponse(status_code=200, content={"message": "Törölve."})


# --- TEREPI ELLENŐRZÉS (bejárás / dokumentáció-felülvizsgálat) ---

@router.get("/vivu/ugyfel/{ugyfel_id}/uj-ellenorzes", response_class=HTMLResponse)
def uj_ellenorzes_oldal(
    ugyfel_id: int,
    request: Request,
    tipus: str = "BEJARAS",
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id, cegek, aktualis_ceg_nev_ha_eltero, sugo_e = _ceg_kontextus(request, current_user, db)
    ugyfel = db.query(models.VivuUgyfel).filter(
        models.VivuUgyfel.id == ugyfel_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if not ugyfel:
        return RedirectResponse(url="/vivu", status_code=303)
    if not _lehet_ellenorizni(current_user, working_company_id, db):
        return RedirectResponse(url=f"/vivu/ugyfel/{ugyfel_id}", status_code=303)

    tipus = tipus if tipus in TIPUSOK else "BEJARAS"
    tetelek = db.query(models.VivuEllenorzesiTetel).filter(
        or_(models.VivuEllenorzesiTetel.company_id == None, models.VivuEllenorzesiTetel.company_id == working_company_id),
        models.VivuEllenorzesiTetel.aktiv == True, models.VivuEllenorzesiTetel.tipus == tipus,
    ).order_by(models.VivuEllenorzesiTetel.szoveg).all()

    telephelyek = [t for t in ugyfel.telephelyek if t.aktiv]

    return templates.TemplateResponse(
        request=request,
        name="vivu_uj_ellenorzes.html",
        context={
            "user": current_user,
            "cegek": cegek,
            "aktualis_ceg_nev_ha_eltero": aktualis_ceg_nev_ha_eltero,
            "sugo_e": sugo_e,
            "working_company_id": working_company_id,
            "ugyfel": ugyfel,
            "tipus": tipus,
            "telephelyek": telephelyek,
            "epuletek_json": _epuletek_json_ugyfelhez(ugyfel, db),
            "tetelek": tetelek,
            "sulyossagok": SULYOSSAGOK,
            "szakterulet_mezok": SZAKTERULET_MEZOK,
            "mai_nap": date.today().isoformat(),
            "celellenorzes_opciok": _celellenorzes_opciok(tetelek),
        }
    )


VIVU_ELLENORZES_STATUSZOK = ("RENDBEN", "SZABALYTALANSAG", "NEM_RELEVANS")


def _vivu_egesz_vagy_hiba(ertek):
    """int(ertek), ha egész szám (vagy számjegyes szöveg); különben ValueError. A bool nem számít egésznek."""
    if isinstance(ertek, bool):
        raise ValueError()
    if isinstance(ertek, int):
        return ertek
    if isinstance(ertek, str) and ertek.strip().isdigit():
        return int(ertek.strip())
    raise ValueError()


def vivu_hivatkozasok_ellenorzese(db, working_company_id, telephely_id, erintett_epulet_idk, tetel_eredmenyek):
    """J-13 (v70): a VIVÜ ellenőrzés-mentés JSON-ból érkező azonosítóinak ellenőrzése MENTÉS ELŐTT. Eddig a sorok tétele (bármely cég
    tétele), épülete (nem volt a telephelyhez kötve) és szintje (nem volt az épülethez kötve) validálatlan volt, a statusz fehérlista
    nélkül mentődött, az érintett épületek között az idegeneket csendben eldobta. Hibánál a 400-as válasz üzenete, különben None.
    Az azonosítókat egész számra normalizálja (a bemeneti listákat helyben módosítja)."""
    if not isinstance(tetel_eredmenyek, list) or not all(isinstance(s, dict) for s in tetel_eredmenyek):
        return "Hibás tétel-adat formátum!"
    # épület-azonosító -> az épület szintjeinek azonosítói, CSAK a megadott telephelyről
    telephely_epulet_szintek = {r[0]: set() for r in db.query(models.VivuEpulet.id).filter(models.VivuEpulet.telephely_id == telephely_id).all()}
    if telephely_epulet_szintek:
        for eid, szid in db.query(models.VivuSzint.epulet_id, models.VivuSzint.id).filter(
                models.VivuSzint.epulet_id.in_(list(telephely_epulet_szintek))).all():
            telephely_epulet_szintek[eid].add(szid)
    try:
        erintett_epulet_idk[:] = [_vivu_egesz_vagy_hiba(x) for x in erintett_epulet_idk]
    except ValueError:
        return "Hibás érintett épület-adat formátum!"
    if any(x not in telephely_epulet_szintek for x in erintett_epulet_idk):
        return "Az érintett épületek között van olyan, ami nem ehhez a telephelyhez tartozik!"
    tetel_idk = {r[0] for r in db.query(models.VivuEllenorzesiTetel.id).filter(
        or_(models.VivuEllenorzesiTetel.company_id == None, models.VivuEllenorzesiTetel.company_id == working_company_id)).all()}

    for sor in tetel_eredmenyek:
        if sor.get("statusz") not in VIVU_ELLENORZES_STATUSZOK:
            return "Érvénytelen tétel-státusz!"
        try:
            sor["tetel_id"] = _vivu_egesz_vagy_hiba(sor.get("tetel_id"))
        except ValueError:
            return "Érvénytelen tétel-azonosító!"
        if sor["tetel_id"] not in tetel_idk:
            return "A tételek között van olyan, ami nem ehhez a céghez tartozik!"
        if sor["statusz"] != "SZABALYTALANSAG":
            continue
        if not sor.get("epulet_id"):
            return "Minden szabálytalanságnál meg kell adni az épületet!"
        try:
            sor["epulet_id"] = _vivu_egesz_vagy_hiba(sor["epulet_id"])
            sor["szint_id"] = _vivu_egesz_vagy_hiba(sor["szint_id"]) if sor.get("szint_id") else None
        except ValueError:
            return "Érvénytelen épület/szint azonosító a tételsorban!"
        if sor["epulet_id"] not in telephely_epulet_szintek:
            return "A szabálytalanság épülete nem ehhez a telephelyhez tartozik!"
        if sor["szint_id"] is not None and sor["szint_id"] not in telephely_epulet_szintek[sor["epulet_id"]]:
            return "A szabálytalanság szintje nem tartozik a megadott épülethez!"
    return None


def vivu_karbantartas_hivatkozasok_ellenorzese(db, company_id, telephely_id, erintett_epulet_idk, torolt_fenykep_idk, tetel_sorok):
    """J-13 (v71): a rendszeradmin VIVÜ-karbantartó mentésének (`/rendszeradmin/vivu/ellenorzes/{id}/karbantartas`) sorazonosítói.
    Megegyezik a v70-es `vivu_hivatkozasok_ellenorzese` szabályaival, de a sorok lehetnek MEGLÉVŐK (id) vagy ÚJAK (tetel_id), és
    törlés-jelzőt is tartalmazhatnak. company_id az ellenőrzés (Megrendelő) cége, nem az RA munkakörnyezete. Hibánál az üzenet, különben None;
    az azonosítókat egész számra normalizálja (a bemeneti listákat helyben módosítja)."""
    if not isinstance(tetel_sorok, list) or not all(isinstance(s, dict) for s in tetel_sorok):
        return "Hibás tétel-adat formátum!"
    try:
        erintett_epulet_idk[:] = [_vivu_egesz_vagy_hiba(x) for x in erintett_epulet_idk]
        torolt_fenykep_idk[:] = [_vivu_egesz_vagy_hiba(x) for x in torolt_fenykep_idk]
    except ValueError:
        return "Hibás érintett épület / törölt fénykép azonosító!"
    telephely_epulet_szintek = {r[0]: set() for r in db.query(models.VivuEpulet.id).filter(models.VivuEpulet.telephely_id == telephely_id).all()}
    if telephely_epulet_szintek:
        for eid, szid in db.query(models.VivuSzint.epulet_id, models.VivuSzint.id).filter(
                models.VivuSzint.epulet_id.in_(list(telephely_epulet_szintek))).all():
            telephely_epulet_szintek[eid].add(szid)
    if any(x not in telephely_epulet_szintek for x in erintett_epulet_idk):
        return "Az érintett épületek között van olyan, ami nem ehhez a telephelyhez tartozik!"
    tetel_idk = {r[0] for r in db.query(models.VivuEllenorzesiTetel.id).filter(
        or_(models.VivuEllenorzesiTetel.company_id == None, models.VivuEllenorzesiTetel.company_id == company_id)).all()}

    for sor in tetel_sorok:
        uj_sor = sor.get("id") is None
        if sor.get("torles") and not uj_sor:
            continue                                   # törlendő MEGLÉVŐ sor: más adata nem számít (az ÚJ sornál a kezelő kód a torles jelzőt nem nézi, ezért ott is validálunk)
        if uj_sor:
            if not sor.get("tetel_id"):
                continue                               # a kezelő kód is kihagyja
            try:
                sor["tetel_id"] = _vivu_egesz_vagy_hiba(sor["tetel_id"])
            except ValueError:
                return "Érvénytelen tétel-azonosító!"
            if sor["tetel_id"] not in tetel_idk:
                return "A tételek között van olyan, ami nem ehhez a céghez tartozik!"
            statusz = "SZABALYTALANSAG"
        else:
            try:
                sor["id"] = _vivu_egesz_vagy_hiba(sor["id"])
            except ValueError:
                return "Érvénytelen tétel-eredmény azonosító!"
            statusz = sor.get("statusz")
            if statusz not in VIVU_ELLENORZES_STATUSZOK:
                return "Érvénytelen tétel-státusz!"
        if statusz != "SZABALYTALANSAG":
            continue
        if not sor.get("epulet_id"):
            return "Minden szabálytalanságnál meg kell adni az épületet!"
        try:
            sor["epulet_id"] = _vivu_egesz_vagy_hiba(sor["epulet_id"])
            sor["szint_id"] = _vivu_egesz_vagy_hiba(sor["szint_id"]) if sor.get("szint_id") else None
        except ValueError:
            return "Érvénytelen épület/szint azonosító a tételsorban!"
        if sor["epulet_id"] not in telephely_epulet_szintek:
            return "A szabálytalanság épülete nem ehhez a telephelyhez tartozik!"
        if sor["szint_id"] is not None and sor["szint_id"] not in telephely_epulet_szintek[sor["epulet_id"]]:
            return "A szabálytalanság szintje nem tartozik a megadott épülethez!"
    return None


@router.post("/vivu/ugyfel/{ugyfel_id}/ellenorzes")
async def ellenorzes_mentese(
    ugyfel_id: int,
    request: Request,
    tipus: str = Form(...),
    telephely_id: int = Form(...),
    erintett_epuletek_json: str = Form("[]"),
    celellenorzes: str = Form(""),
    datum: str = Form(""),
    mindenrendben: str = Form(""),
    tetel_eredmenyek_json: str = Form(...),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id = get_working_company_id(current_user, request)
    ugyfel = db.query(models.VivuUgyfel).filter(
        models.VivuUgyfel.id == ugyfel_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if not ugyfel or telephely_id not in [t.id for t in ugyfel.telephelyek]:
        return JSONResponse(status_code=404, content={"message": "Megrendelő vagy telephely nem található!"})
    if not _lehet_ellenorizni(current_user, working_company_id, db):
        return JSONResponse(status_code=403, content={"message": "Nincs jogosultságod ehhez a megrendelőhöz!"})

    tipus = tipus if tipus in TIPUSOK else "BEJARAS"

    try:
        tetel_eredmenyek = json.loads(tetel_eredmenyek_json)
    except Exception:
        return JSONResponse(status_code=400, content={"message": "Hibás tétel-adat formátum!"})

    try:
        erintett_epulet_idk = json.loads(erintett_epuletek_json)
        if not isinstance(erintett_epulet_idk, list):
            raise ValueError()
    except Exception:
        return JSONResponse(status_code=400, content={"message": "Hibás érintett épület-adat formátum!"})

    # J-13 (v70): a hivatkozott azonosítók ellenőrzése MENTÉS ELŐTT (hibánál 400, semmi sem keletkezik)
    hivatkozas_hiba = vivu_hivatkozasok_ellenorzese(db, working_company_id, telephely_id, erintett_epulet_idk, tetel_eredmenyek)
    if hivatkozas_hiba:
        return JSONResponse(status_code=400, content={"message": hivatkozas_hiba})

    form_data = await request.form()

    try:
        ellenorzes = models.VivuEllenorzes(
            telephely_id=telephely_id,
            tipus=tipus,
            celellenorzes=celellenorzes or None,
            datum=_parse_date(datum) or date.today(),
            ellenorzo_user_id=current_user.id,
            mindenrendben=(mindenrendben == "true"),
        )
        db.add(ellenorzes)
        db.flush()

        if erintett_epulet_idk:
            ellenorzes.epuletek = db.query(models.VivuEpulet).join(models.VivuTelephely).filter(
                models.VivuEpulet.id.in_(erintett_epulet_idk), models.VivuTelephely.id == telephely_id,
            ).all()

        mappa = f"uploads/{working_company_id}/vivu/{ellenorzes.id}"

        for sor_idx, sor in enumerate(tetel_eredmenyek):
            tetel_id = sor.get("tetel_id")
            statusz = sor.get("statusz")

            eredmeny = models.VivuEllenorzesTetel(
                ellenorzes_id=ellenorzes.id, tetel_id=tetel_id, statusz=statusz,
            )

            if statusz == "SZABALYTALANSAG":
                eredmeny.epulet_id = sor.get("epulet_id") or None
                eredmeny.szint_id = sor.get("szint_id") or None
                eredmeny.helyiseg = (sor.get("helyiseg") or "").strip() or None
                if not eredmeny.epulet_id:
                    raise ValueError("Minden szabálytalanságnál meg kell adni az épületet!")
                eredmeny.sulyossag = sor.get("sulyossag") if sor.get("sulyossag") in SULYOSSAGOK else None
                eredmeny.leiras = sor.get("leiras") or None
                for mezo, _cimke in SZAKTERULET_MEZOK:
                    setattr(eredmeny, mezo, bool(sor.get(mezo)))

                db.add(eredmeny)
                db.flush()

                fajl_index = 0
                while True:
                    fenykep = form_data.get(f"fenykep_{sor_idx}_{fajl_index}")
                    if fenykep is None:
                        break
                    if getattr(fenykep, "filename", "") != "":
                        if not is_safe_extension(fenykep.filename):
                            raise ValueError("Nem engedélyezett fájlformátum a fényképnél!")
                        safe_filename = sanitize_filename(fenykep.filename).rsplit(".", 1)[0] + ".jpg"
                        kep_bytes = tomorit_kepet(await fenykep.read())
                        if kep_bytes is None:
                            raise ValueError("A feltöltött fájl nem érvényes kép!")
                        os.makedirs(mappa, exist_ok=True)
                        file_loc = f"{mappa}/{datetime.now().strftime('%Y%m%d%H%M%S')}_{sor_idx}_{fajl_index}_{safe_filename}"
                        with open(file_loc, "wb") as f:
                            f.write(kep_bytes)
                        db.add(models.VivuTetelFenykep(
                            tetel_id=eredmeny.id, file_path="/" + file_loc, feltolto_user_id=current_user.id,
                        ))
                    fajl_index += 1
            else:
                db.add(eredmeny)

        db.commit()
    except Exception as e:
        db.rollback()
        print(f"❌ Hiba a VIVÜ ellenőrzés mentése közben: {type(e).__name__}: {e}")
        return JSONResponse(status_code=400, content={"message": hiba_uzenet("Hiba történt a mentés közben!", "E-VIVU-01")})

    return JSONResponse(status_code=200, content={"message": "Ellenőrzés elmentve.", "ellenorzes_id": ellenorzes.id})


@router.get("/vivu/ellenorzes/{ellenorzes_id}", response_class=HTMLResponse)
def ellenorzes_reszletek(
    ellenorzes_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id, cegek, aktualis_ceg_nev_ha_eltero, sugo_e = _ceg_kontextus(request, current_user, db)
    ellenorzes = db.query(models.VivuEllenorzes).join(models.VivuTelephely).join(models.VivuUgyfel).filter(
        models.VivuEllenorzes.id == ellenorzes_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if not ellenorzes:
        return RedirectResponse(url="/vivu", status_code=303)
    if not _ugyfel_lathato_e(current_user, ellenorzes.telephely.ugyfel):
        return RedirectResponse(url="/vivu", status_code=303)

    szabalytalansagok = [t for t in ellenorzes.tetel_eredmenyek if t.statusz == "SZABALYTALANSAG"]
    rendben_tetelek = [t for t in ellenorzes.tetel_eredmenyek if t.statusz == "RENDBEN"]
    nem_relevans_tetelek = [t for t in ellenorzes.tetel_eredmenyek if t.statusz == "NEM_RELEVANS"]

    feltolthet_e = (
        current_user.role in FENYKEP_FELTOLTHET_SZEREPKOROK
        and current_user.company_id == ellenorzes.telephely.ugyfel.company_id
    ) or current_user.role in ADMIN_SZEREPKOROK

    return templates.TemplateResponse(
        request=request,
        name="vivu_ellenorzes_reszletek.html",
        context={
            "user": current_user,
            "cegek": cegek,
            "aktualis_ceg_nev_ha_eltero": aktualis_ceg_nev_ha_eltero,
            "sugo_e": sugo_e,
            "working_company_id": working_company_id,
            "ellenorzes": ellenorzes,
            "szabalytalansagok": szabalytalansagok,
            "rendben_tetelek": rendben_tetelek,
            "nem_relevans_tetelek": nem_relevans_tetelek,
            "feltolthet_e": feltolthet_e,
            "szakterulet_mezok": SZAKTERULET_MEZOK,
        }
    )


@router.post("/vivu/tetel/{tetel_id}/fenykep")
async def tetel_fenykep_feltoltese(
    tetel_id: int,
    request: Request,
    fenykep: UploadFile = File(...),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    """Utólagos fénykép-csatolás egy már rögzített szabálytalansághoz - mert a legtöbb
    telephelyen nem engedik a helyszíni fényképezést, a képek csak később, más csatornán
    érkeznek. VIVU ügyintéző és Admin egyaránt jogosult, csak a saját
    cégéhez tartozó tételekhez."""
    tetel = db.query(models.VivuEllenorzesTetel).join(models.VivuEllenorzes).join(models.VivuTelephely).join(models.VivuUgyfel).filter(
        models.VivuEllenorzesTetel.id == tetel_id
    ).first()
    if not tetel:
        return JSONResponse(status_code=404, content={"message": "A tétel nem található!"})

    working_company_id = get_working_company_id(current_user, request)
    if tetel.ellenorzes.telephely.ugyfel.company_id != working_company_id:
        return JSONResponse(status_code=403, content={"message": "Nincs jogosultságod ehhez a megrendelőhöz!"})
    if current_user.role not in FENYKEP_FELTOLTHET_SZEREPKOROK:
        return JSONResponse(status_code=403, content={"message": "Nincs jogosultságod fényképet csatolni!"})
    # J-09 (v65): inaktív cégnél / csak_olvasas fiókkal nincs utólagos fotó-csatolás sem
    tiltas = irasi_tiltas_oka(current_user, working_company_id, db)
    if tiltas:
        return JSONResponse(status_code=403, content={"message": IRASI_TILTAS_UZENETEK[tiltas]})

    if not is_safe_extension(fenykep.filename):
        return JSONResponse(status_code=400, content={"message": "Nem engedélyezett fájlformátum!"})

    ellenorzes_id = tetel.ellenorzes_id
    mappa = f"uploads/{working_company_id}/vivu/{ellenorzes_id}"
    safe_filename = sanitize_filename(fenykep.filename).rsplit(".", 1)[0] + ".jpg"
    kep_bytes = tomorit_kepet(await fenykep.read())
    if kep_bytes is None:
        return JSONResponse(status_code=400, content={"message": "A feltöltött fájl nem érvényes kép!"})
    os.makedirs(mappa, exist_ok=True)
    file_loc = f"{mappa}/{datetime.now().strftime('%Y%m%d%H%M%S')}_utolagos_{safe_filename}"
    with open(file_loc, "wb") as f:
        f.write(kep_bytes)

    uj_kep = models.VivuTetelFenykep(
        tetel_id=tetel_id, file_path="/" + file_loc, feltolto_user_id=current_user.id,
    )
    db.add(uj_kep)

    db.add(models.AuditLog(
        company_id=working_company_id, user_id=current_user.id,
        field_name="VIVÜ FÉNYKÉP UTÓLAGOS FELTÖLTÉSE",
        old_value="-", new_value=f"Ellenőrzési tétel #{tetel_id} (ellenőrzés #{ellenorzes_id})",
    ))
    db.commit()
    db.refresh(uj_kep)

    return JSONResponse(status_code=200, content={"message": "Fénykép hozzáadva.", "file_path": uj_kep.file_path})


@router.delete("/vivu/tetel-fenykep/{fenykep_id}")
def tetel_fenykep_torlese(
    fenykep_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    kep = db.query(models.VivuTetelFenykep).join(models.VivuEllenorzesTetel).join(models.VivuEllenorzes).join(models.VivuTelephely).join(models.VivuUgyfel).filter(
        models.VivuTetelFenykep.id == fenykep_id
    ).first()
    if not kep:
        return JSONResponse(status_code=404, content={"message": "A fénykép nem található!"})

    working_company_id = get_working_company_id(current_user, request)
    if kep.tetel.ellenorzes.telephely.ugyfel.company_id != working_company_id:
        return JSONResponse(status_code=403, content={"message": "Nincs jogosultságod ehhez a megrendelőhöz!"})
    if current_user.role not in FENYKEP_FELTOLTHET_SZEREPKOROK:
        return JSONResponse(status_code=403, content={"message": "Nincs jogosultságod fényképet törölni!"})
    if not is_company_active(working_company_id, db):
        return JSONResponse(status_code=403, content={"message": "Ez a cég inaktív státuszú, törlés nem lehetséges!"})
    if current_user.csak_olvasas:
        return JSONResponse(status_code=403, content={"message": "Csak megtekintési jogosultsággal rendelkezel, szerkesztés nem lehetséges!"})

    fajl = kep.file_path.lstrip("/") if kep.file_path else None
    if fajl and os.path.exists(fajl):
        try:
            os.remove(fajl)
        except OSError:
            pass

    db.delete(kep)
    db.commit()
    return JSONResponse(status_code=200, content={"message": "Fénykép törölve."})


@router.get("/vivu/ellenorzes/{ellenorzes_id}/pdf")
def ellenorzes_pdf(
    ellenorzes_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id = get_working_company_id(current_user, request)
    ellenorzes = db.query(models.VivuEllenorzes).join(models.VivuTelephely).join(models.VivuUgyfel).filter(
        models.VivuEllenorzes.id == ellenorzes_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if not ellenorzes:
        return RedirectResponse(url="/vivu", status_code=303)
    if not _ugyfel_lathato_e(current_user, ellenorzes.telephely.ugyfel):
        return RedirectResponse(url="/vivu", status_code=303)

    szabalytalansagok = [t for t in ellenorzes.tetel_eredmenyek if t.statusz == "SZABALYTALANSAG"]
    rendben_tetelek = [t for t in ellenorzes.tetel_eredmenyek if t.statusz == "RENDBEN"]
    nem_relevans_tetelek = [t for t in ellenorzes.tetel_eredmenyek if t.statusz == "NEM_RELEVANS"]

    pdf = VivuEllenorzesPDF(ellenorzes)
    pdf.alias_nb_pages()
    pdf.add_page()
    pdf.alapadatok()

    cim = "dokumentáció-felülvizsgálaton" if ellenorzes.tipus == "DOKUMENTACIO" else "bejáráson"
    if szabalytalansagok:
        pdf.set_font("DejaVu", "B", 12)
        pdf.cell(0, 8, f"Szabálytalanságok ({len(szabalytalansagok)})", ln=True)
        pdf.ln(1)
        for sz in szabalytalansagok:
            pdf.szabalytalansag_blokk(sz)
    else:
        pdf.set_font("DejaVu", "", 10)
        pdf.cell(0, 7, f"Nem történt szabálytalanság ezen a(z) {cim}.", ln=True)

    pdf.egyszeru_lista(f"Rendben talált tételek ({len(rendben_tetelek)})", rendben_tetelek)
    pdf.egyszeru_lista(f"Nem releváns tételek ({len(nem_relevans_tetelek)})", nem_relevans_tetelek)

    pdf_bytes = bytes(pdf.output())
    return StreamingResponse(
        io.BytesIO(pdf_bytes), media_type="application/pdf",
        headers={"Content-Disposition": f"inline; filename=vivu_ellenorzes_{ellenorzes_id}.pdf"}
    )


@router.get("/vivu/ugyfel/{ugyfel_id}/napi-osszefoglalo/pdf")
def napi_osszefoglalo_pdf(
    ugyfel_id: int,
    request: Request,
    datum: str,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id = get_working_company_id(current_user, request)
    ugyfel = db.query(models.VivuUgyfel).filter(
        models.VivuUgyfel.id == ugyfel_id, models.VivuUgyfel.company_id == working_company_id
    ).first()
    if not ugyfel:
        return RedirectResponse(url="/vivu", status_code=303)
    if not _ugyfel_lathato_e(current_user, ugyfel):
        return RedirectResponse(url="/vivu", status_code=303)

    datum_obj = _parse_date(datum) or date.today()

    ellenorzesek = db.query(models.VivuEllenorzes).join(models.VivuTelephely).options(
        joinedload(models.VivuEllenorzes.telephely)
    ).filter(
        models.VivuTelephely.ugyfel_id == ugyfel_id, models.VivuEllenorzes.datum == datum_obj
    ).all()

    telephely_szamlalo = {}
    for e in ellenorzesek:
        nev = e.telephely.nev if e.telephely else "-"
        telephely_szamlalo[nev] = telephely_szamlalo.get(nev, 0) + 1

    ellenorzes_idk = [e.id for e in ellenorzesek]
    szabalytalansagok = db.query(models.VivuEllenorzesTetel).options(
        joinedload(models.VivuEllenorzesTetel.tetel)
    ).filter(
        models.VivuEllenorzesTetel.ellenorzes_id.in_(ellenorzes_idk),
        models.VivuEllenorzesTetel.statusz == "SZABALYTALANSAG",
    ).all() if ellenorzes_idk else []

    tetel_szamlalo = {}
    sulyossag_szamlalo = {"ENYHE": 0, "KOZEPES": 0, "SULYOS": 0}
    for sz in szabalytalansagok:
        tetel_szoveg = sz.tetel.szoveg if sz.tetel else "Ismeretlen"
        tetel_szamlalo[tetel_szoveg] = tetel_szamlalo.get(tetel_szoveg, 0) + 1
        if sz.sulyossag in sulyossag_szamlalo:
            sulyossag_szamlalo[sz.sulyossag] += 1

    pdf = VivuNapiOsszefoglaloPDF(ugyfel, datum_obj)
    pdf.alias_nb_pages()
    pdf.add_page()
    pdf.diagramok_hozzaadasa(
        telephely_adatok=sorted(telephely_szamlalo.items(), key=lambda x: -x[1]),
        tetel_adatok=sorted(tetel_szamlalo.items(), key=lambda x: -x[1])[:8],
        sulyossag_adatok=[(SULYOSSAG_CIMKE[k], v) for k, v in sulyossag_szamlalo.items()],
    )

    pdf.tablazat_fejlec(f"Elvégzett ellenőrzések ({len(ellenorzesek)})")
    pdf.ellenorzesek_tablazata(ellenorzesek)

    if szabalytalansagok:
        pdf.tablazat_fejlec(f"Szabálytalanságok részletei ({len(szabalytalansagok)})")
        pdf.szabalytalansagok_tablazata(szabalytalansagok)

    pdf_bytes = bytes(pdf.output())
    return StreamingResponse(
        io.BytesIO(pdf_bytes), media_type="application/pdf",
        headers={"Content-Disposition": f"inline; filename=vivu_napi_osszefoglalo_{ugyfel_id}_{datum_obj}.pdf"}
    )


# --- CÉGES BEÁLLÍTÁSOK (áttekintő oldal: VIVÜ ügyintéző, megtekintők, gyors linkek) ---

@router.get("/vivu/beallitasok", response_class=HTMLResponse)
def vivu_ceges_beallitasok(
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    working_company_id, cegek, aktualis_ceg_nev_ha_eltero, sugo_e = _ceg_kontextus(request, current_user, db)
    if not _lehet_adminisztralni(current_user, working_company_id, db):
        return RedirectResponse(url="/vivu", status_code=303)

    vivu_ugyintezo = db.query(models.User).filter(
        models.User.company_id == working_company_id, models.User.role == "VIVU", models.User.aktiv == True,
    ).first()
    megtekintok = db.query(models.User).filter(
        models.User.company_id == working_company_id, models.User.role == "Megtekintő"
    ).order_by(models.User.username).all()

    megtekintok_ugyfelei = {
        u.id: [ug for ug in u.vivu_ugyfelek if ug.company_id == working_company_id]
        for u in megtekintok
    }

    ugyfelek = db.query(models.VivuUgyfel).filter(
        models.VivuUgyfel.company_id == working_company_id, models.VivuUgyfel.aktiv == True,
    ).order_by(models.VivuUgyfel.nev).all()

    return templates.TemplateResponse(
        request=request,
        name="vivu_ceges_beallitasok.html",
        context={
            "user": current_user,
            "cegek": cegek,
            "aktualis_ceg_nev_ha_eltero": aktualis_ceg_nev_ha_eltero,
            "sugo_e": sugo_e,
            "working_company_id": working_company_id,
            "vivu_ugyintezo": vivu_ugyintezo,
            "megtekintok": megtekintok,
            "megtekintok_ugyfelei": megtekintok_ugyfelei,
            "ugyfelek": ugyfelek,
        }
    )
