import os
import re
import traceback
import uuid
from pathlib import Path
from typing import List

from fastapi import APIRouter, Request, Depends, Form, File, UploadFile
from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse, Response
from fpdf import FPDF
from sqlalchemy.orm import Session

import models
from database import get_db
from security import get_current_user, is_rendszeradminisztrator, sanitize_filename, get_allowed_companies_list, tartalom_illeszkedik_e_kiterjesztesre
from hibakodok import hiba_uzenet
from image_utils import kep_e, tomorit_kepet
from templates_env import templates, helyi_most

router = APIRouter()

STATIC_DIR = Path(__file__).resolve().parent.parent / "static"

# Ugyanaz a dokumentumtípus-katalógus, mint az egygépes feltöltésnél (templates/list.html #uploadFileForm),
# hogy a céges beállítások és a tömeges import ne mutasson eltérő katalógust.
# v78: + Fénykép - Adattábla (kép), Használati utasítás / Gépkönyv / Adatlap (PDF). A KÉP típusok: FOTO_TIPUSOK.
DOKUMENTUM_TIPUSOK = [
    ("Fénykép", "Fénykép"),
    ("Fénykép - Adattábla", "Fénykép - Adattábla (gépazonosító)"),
    ("Gépnapló", "Gépnapló / Ellenőrzési füzet"),
    ("Selejtezési jkv", "Selejtezési jegyzőkönyv"),
    ("Adás-vételi", "Adás-vételi szerződés"),
    ("Forgalmi engedély", "Forgalmi engedély másolata"),
    ("Üzembehelyezési jkv / Ismételt üzembehelyezés", "Üzembehelyezési jkv / Ismételt üzembehelyezés"),
    ("Szerkezeti vizsgálat jkv", "Szerkezeti vizsgálat jkv (Külső/Szkennelt)"),
    ("Fővizsgálat jkv", "Fővizsgálat jkv (Külső/Szkennelt)"),
    ("Időszakos biztonsági felülvizsgálat jkv", "Időszakos biztonsági felülvizsgálat jkv"),
    ("Rendkívüli vizsgálat jkv", "Rendkívüli vizsgálat jkv"),
    ("Használati utasítás", "Használati utasítás (kezelési, karbantartási)"),
    ("Gépkönyv", "Gépkönyv"),
    ("Adatlap", "Adatlap (műszaki adatlap)"),
    ("Egyéb", "Egyéb dokumentum"),
]
# Ezeknél a típusoknál KÉP (jpg/png/gif/webp), a többinél PDF fogadható el
FOTO_TIPUSOK = ("Fénykép", "Fénykép - Adattábla")
KEP_KITERJESZTESEK = {".jpg", ".jpeg", ".png", ".gif", ".webp"}
DOKUMENTUM_KITERJESZTESEK = {".pdf"}

STAGING_MAPPA = "uploads/_dokumentum_import_staging"

STATUS_CIMKE = {
    "sikeres": "Sikeres",
    "nincs_talalat": "Nincs találat",
    "hibas_formatum": "Hibás formátum",
    "hiba": "Hiba",
    "tobbszoros_talalat": "Több gép illeszkedik",
    "tobbszoros_mentve": "Sikeres (feloldva)",
    "tobbszoros_kihagyva": "Kihagyva",
}


def _rendszeradmin_kapu(current_user):
    return is_rendszeradminisztrator(current_user)


def _megengedett_kiterjesztes_e(filename: str, dokumentum_tipus: str) -> bool:
    kiterjesztes = os.path.splitext(filename.lower())[1]
    if dokumentum_tipus in FOTO_TIPUSOK:
        return kiterjesztes in KEP_KITERJESZTESEK
    return kiterjesztes in DOKUMENTUM_KITERJESZTESEK


def _normalizalt(ertek: str | None) -> str:
    """Szóköz- és kis-nagybetű-független összevetéshez: a többszörös/szélső szóközöket
    összevonja, mindent nagybetűsít."""
    return re.sub(r"\s+", " ", (ertek or "").strip()).upper()


def _keres_gepeket_fajlnevben(fajlnev: str, gepek: list) -> list:
    """A fájlnév (kiterjesztés nélküli) elejét veti össze a cég gépeinek gyári számával,
    szóköz/kis-nagybetű különbségre érzéketlenül. Megengedi, hogy a gyári szám után még
    álljon extra szöveg a fájlnévben (pl. "SJ201006 10 B02-011.pdf" -> gyári szám "SJ201006 10"),
    amíg az egy természetes határon (szóköz/aláhúzás/kötőjel/fájlnév vége) válik el tőle.
    A leghosszabb illeszkedő gyári számú gép(ek)et adja vissza - ha több gép is ugyanolyan
    hosszan (pl. azonos gyári szám miatt) illeszkedik, mindegyiket visszaadja."""
    torzs = os.path.splitext(fajlnev)[0]
    torzs_norm = _normalizalt(torzs)

    talalatok, legjobb_hossz = [], 0
    for gep in gepek:
        gsz_norm = _normalizalt(gep.gyari_szam)
        if not gsz_norm or not torzs_norm.startswith(gsz_norm):
            continue
        utana = torzs_norm[len(gsz_norm):]
        if utana and utana[0] not in " _-":
            continue
        if len(gsz_norm) > legjobb_hossz:
            talalatok, legjobb_hossz = [gep], len(gsz_norm)
        elif len(gsz_norm) == legjobb_hossz:
            talalatok.append(gep)
    return talalatok


def _mentse_fajlt_gephez(db: Session, gep: models.Machine, filename: str, tartalom: bytes, dokumentum_tipus: str, current_user_id: int | None):
    mappa = f"uploads/{gep.company_id}/{gep.id}"
    os.makedirs(mappa, exist_ok=True)
    safe_filename = sanitize_filename(filename)

    if kep_e(filename):
        tartalom = tomorit_kepet(tartalom)
        if tartalom is None:
            raise ValueError("A fájl nem érvényes kép (a kiterjesztése alapján annak tűnne)!")
        safe_filename = safe_filename.rsplit(".", 1)[0] + ".jpg"

    file_location = f"{mappa}/{safe_filename}"
    # Ha ilyen nevű fájl már létezik ezen a helyen (pl. korábbi kézi feltöltésből, esetleg
    # más jogosultsággal), NEM próbáljuk felülírni - inkább egyedi nevet adunk neki, hogy
    # elkerüljük a felülírásból eredő jogosultsági hibát és az esetleges adatvesztést.
    if os.path.exists(file_location):
        torzs, kiterjesztes = os.path.splitext(safe_filename)
        safe_filename = f"{torzs}_{uuid.uuid4().hex[:8]}{kiterjesztes}"
        file_location = f"{mappa}/{safe_filename}"

    with open(file_location, "wb") as f:
        f.write(tartalom)

    db.add(models.File(machine_id=gep.id, file_path="/" + file_location, file_type=dokumentum_tipus))
    db.add(models.AuditLog(
        machine_id=gep.id, user_id=current_user_id, field_name="DOKUMENTUM FELTÖLTÉS",
        old_value="-", new_value=f"Feltöltött dokumentum: {dokumentum_tipus} (tömeges import: {filename})",
    ))


@router.get("/rendszeradmin/dokumentum-import", response_class=HTMLResponse)
def dokumentum_import_oldal(
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    if not _rendszeradmin_kapu(current_user):
        return RedirectResponse(url="/valassz-modult", status_code=303)

    cegek = [c for c in get_allowed_companies_list(current_user, db, "EMELOGEP") if c.id != 1]

    return templates.TemplateResponse(
        request=request,
        name="dokumentum_import.html",
        context={
            "user": current_user,
            "cegek": cegek,
            "dokumentum_tipusok": DOKUMENTUM_TIPUSOK,
        }
    )


@router.post("/rendszeradmin/dokumentum-import/feltoltes")
async def dokumentum_import_feltoltes(
    company_id: int = Form(...),
    dokumentum_tipus: str = Form(...),
    files: List[UploadFile] = File(...),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    if not _rendszeradmin_kapu(current_user):
        return JSONResponse(status_code=403, content={"message": "Csak a rendszeradminisztrátor!"})

    if dokumentum_tipus not in dict(DOKUMENTUM_TIPUSOK):
        return JSONResponse(status_code=400, content={"message": "Ismeretlen dokumentumtípus!"})

    ceg = db.query(models.Company).filter(models.Company.id == company_id).first()
    if not ceg:
        return JSONResponse(status_code=404, content={"message": "Cég nem található!"})

    sikeres, hibas_formatum, nincs_talalat, tobbszoros_talalat, hiba = [], [], [], [], []
    gepek = db.query(models.Machine).filter(models.Machine.company_id == company_id).all()

    try:
        os.makedirs(STAGING_MAPPA, exist_ok=True)
    except Exception as e:
        print(f"❌ Hiba a dokumentum-import staging mappa létrehozásakor: {type(e).__name__}: {e}")
        return JSONResponse(status_code=400, content={"message": hiba_uzenet("Hiba történt a feltöltés közben!", "E-DOK-01")})

    for file in files:
        filename = file.filename
        tartalom = await file.read()

        if not _megengedett_kiterjesztes_e(filename, dokumentum_tipus):
            elvart = "kép (jpg/png/gif/webp)" if dokumentum_tipus in FOTO_TIPUSOK else "PDF"
            hibas_formatum.append({"filename": filename, "ok": f"Nem megfelelő fájlformátum ehhez a típushoz - {elvart} vártunk."})
            continue

        if dokumentum_tipus not in FOTO_TIPUSOK and not tartalom_illeszkedik_e_kiterjesztesre(filename, tartalom[:8]):
            hibas_formatum.append({"filename": filename, "ok": "A fájl tartalma nem egyezik a kiterjesztésével (nem valódi PDF)."})
            continue

        # Az egyes fájlok mentését (lemezműveletek: mappa-létrehozás, fájlírás) fájlonként
        # próbáljuk - ha egy adott fájlnál lemez-/jogosultsági hiba történik, az ne akassza
        # meg a köteg többi, egyébként rendben lévő fájljának feltöltését.
        try:
            talalatok = _keres_gepeket_fajlnevben(filename, gepek)

            if not talalatok:
                nincs_talalat.append({"filename": filename, "gyari_szam": None, "ok": "A fájlnévhez nem található illeszkedő gyári számú gép ennél a cégnél."})
                continue

            if len(talalatok) > 1:
                staging_filename = f"{uuid.uuid4().hex}_{sanitize_filename(filename)}"
                with open(f"{STAGING_MAPPA}/{staging_filename}", "wb") as f:
                    f.write(tartalom)
                tobbszoros_talalat.append({
                    "filename": filename, "gyari_szam": talalatok[0].gyari_szam, "staging_filename": staging_filename,
                    "jeloltek": [{"id": m.id, "megnevezes": m.megnevezes, "gyari_szam": m.gyari_szam} for m in talalatok],
                })
                continue

            gep = talalatok[0]
            mar_letezik = db.query(models.File).filter(
                models.File.machine_id == gep.id, models.File.file_type == dokumentum_tipus
            ).all()
            # Egyszerű duplikáció-ellenőrzés fájlnév alapján (a kép-típusúak a mentéskor .jpg-re
            # konvertálódnak, ezért ott csak a kiterjesztés előtti névtörzset hasonlítjuk).
            torzs = sanitize_filename(filename).rsplit(".", 1)[0]
            if any(torzs in (f.file_path or "") for f in mar_letezik):
                nincs_talalat.append({"filename": filename, "gyari_szam": gep.gyari_szam, "ok": f"Már fel van töltve ehhez a géphez (gép: {gep.megnevezes}) - kihagyva."})
                continue

            _mentse_fajlt_gephez(db, gep, filename, tartalom, dokumentum_tipus, current_user.id)
            db.commit()
            sikeres.append({"filename": filename, "gyari_szam": gep.gyari_szam, "gep_megnevezes": gep.megnevezes, "gep_id": gep.id})
        except Exception as e:
            db.rollback()
            print(f"❌ Hiba a(z) '{filename}' fájl mentésekor: {type(e).__name__}: {e}")
            hiba.append({"filename": filename, "ok": hiba_uzenet("Hiba történt a fájl mentése közben.", "E-DOK-02")})

    return JSONResponse(status_code=200, content={
        "sikeres": sikeres, "hibas_formatum": hibas_formatum,
        "nincs_talalat": nincs_talalat, "tobbszoros_talalat": tobbszoros_talalat, "hiba": hiba,
    })


@router.post("/rendszeradmin/dokumentum-import/konfliktus-feloldas")
async def dokumentum_import_konfliktus_feloldas(
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    if not _rendszeradmin_kapu(current_user):
        return JSONResponse(status_code=403, content={"message": "Csak a rendszeradminisztrátor!"})

    adat = await request.json()
    company_id = adat.get("company_id")
    dokumentum_tipus = adat.get("dokumentum_tipus")
    dontesek = adat.get("dontesek", [])

    if dokumentum_tipus not in dict(DOKUMENTUM_TIPUSOK):
        return JSONResponse(status_code=400, content={"message": "Ismeretlen dokumentumtípus!"})

    eredmeny = []
    for dontes in dontesek:
        staging_filename = os.path.basename(dontes.get("staging_filename", ""))
        eredeti_filename = dontes.get("filename", staging_filename)
        machine_id = dontes.get("machine_id")
        staging_utvonal = f"{STAGING_MAPPA}/{staging_filename}"

        if not os.path.isfile(staging_utvonal):
            eredmeny.append({"filename": eredeti_filename, "allapot": "hiba", "ok": "A ideiglenesen tárolt fájl már nem elérhető."})
            continue

        if not machine_id:
            os.remove(staging_utvonal)
            eredmeny.append({"filename": eredeti_filename, "allapot": "tobbszoros_kihagyva"})
            continue

        # Fájlonkénti hibakezelés itt is - egy sikertelen mentés ne akassza meg a többi,
        # ugyanebben a kötegben feloldásra váró fájl mentését.
        try:
            gep = db.query(models.Machine).filter(
                models.Machine.id == machine_id, models.Machine.company_id == company_id
            ).first()
            if not gep:
                eredmeny.append({"filename": eredeti_filename, "allapot": "hiba", "ok": "A kiválasztott gép nem található ennél a cégnél."})
                continue

            with open(staging_utvonal, "rb") as f:
                tartalom = f.read()
            _mentse_fajlt_gephez(db, gep, eredeti_filename, tartalom, dokumentum_tipus, current_user.id)
            db.commit()
            os.remove(staging_utvonal)
            eredmeny.append({"filename": eredeti_filename, "allapot": "tobbszoros_mentve", "gep_megnevezes": gep.megnevezes})
        except Exception as e:
            db.rollback()
            print(f"❌ Hiba a(z) '{eredeti_filename}' fájl konfliktus-feloldásakor: {type(e).__name__}: {e}")
            eredmeny.append({"filename": eredeti_filename, "allapot": "hiba", "ok": hiba_uzenet("Hiba történt a fájl mentése közben.", "E-DOK-03")})

    return JSONResponse(status_code=200, content={"eredmeny": eredmeny})


def _rovidit(szoveg: str | None, max_hossz: int) -> str:
    szoveg = szoveg or "-"
    return szoveg if len(szoveg) <= max_hossz else szoveg[:max_hossz - 1] + "…"


class DokumentumImportRiportPDF(FPDF):
    """A tömeges dokumentum-import egy futásának összefoglaló jelentése: melyik fájl
    melyik géphez került (vagy miért nem), egy táblázatban."""

    def __init__(self, ceg_nev: str, dokumentum_tipus: str):
        super().__init__(orientation="P", format="A4")
        self.ceg_nev = ceg_nev
        self.dokumentum_tipus = dokumentum_tipus
        self.add_font("DejaVu", "", str(STATIC_DIR / "DejaVuSans.ttf"))
        self.add_font("DejaVu", "B", str(STATIC_DIR / "DejaVuSans-Bold.ttf"))
        self.alias_nb_pages()

    def header(self):
        self.set_font("DejaVu", "B", 14)
        self.cell(0, 9, "Dokumentum import - jelentés", ln=True, align="C")
        self.set_font("DejaVu", "", 10)
        self.cell(0, 6, f"{self.ceg_nev} - {self.dokumentum_tipus}", ln=True, align="C")
        self.cell(0, 6, helyi_most().strftime("%Y-%m-%d %H:%M"), ln=True, align="C")
        self.ln(2)

    def footer(self):
        self.set_y(-15)
        self.set_font("DejaVu", "", 8)
        self.cell(0, 10, "EmeLOG Nyilvántartó rendszerrel készült", align="L")
        self.set_x(-30)
        self.cell(0, 10, f"Oldal {self.page_no()}/{{nb}}", align="R")

    def osszegzo(self, sorok: list):
        szamlalo = {}
        for sor in sorok:
            szamlalo[sor.get("allapot")] = szamlalo.get(sor.get("allapot"), 0) + 1
        self.set_font("DejaVu", "", 10)
        resz = [f"{STATUS_CIMKE.get(k, k)}: {v}" for k, v in szamlalo.items()]
        self.multi_cell(0, 6, f"Összesen {len(sorok)} fájl. " + ", ".join(resz), align="L")
        self.ln(2)

    def tablazat(self, sorok: list):
        oszlopok = [("Fájlnév", 55, "filename"), ("Állapot", 32, "allapot"), ("Gép", 45, "gep_megnevezes"), ("Megjegyzés", 58, "ok")]
        self.set_font("DejaVu", "B", 9)
        self.set_fill_color(230, 230, 230)
        for cim, szel, _ in oszlopok:
            self.cell(szel, 7, cim, border=1, fill=True)
        self.ln()
        self.set_font("DejaVu", "", 8)
        for sor in sorok:
            if self.get_y() > 275:
                self.add_page()
                self.set_font("DejaVu", "B", 9)
                self.set_fill_color(230, 230, 230)
                for cim, szel, _ in oszlopok:
                    self.cell(szel, 7, cim, border=1, fill=True)
                self.ln()
                self.set_font("DejaVu", "", 8)
            for cim, szel, kulcs in oszlopok:
                ertek = STATUS_CIMKE.get(sor.get("allapot"), sor.get("allapot")) if kulcs == "allapot" else sor.get(kulcs)
                karakter_limit = max(10, int(szel / 1.8))
                self.cell(szel, 6, _rovidit(ertek, karakter_limit), border=1)
            self.ln()


@router.post("/rendszeradmin/dokumentum-import/riport-pdf")
async def dokumentum_import_riport_pdf(
    request: Request,
    current_user: models.User = Depends(get_current_user)
):
    if not _rendszeradmin_kapu(current_user):
        return JSONResponse(status_code=403, content={"message": "Csak a rendszeradminisztrátor!"})

    adat = await request.json()
    ceg_nev = adat.get("ceg_nev", "-")
    dokumentum_tipus = adat.get("dokumentum_tipus", "-")
    sorok = adat.get("sorok", [])

    try:
        pdf = DokumentumImportRiportPDF(ceg_nev, dokumentum_tipus)
        pdf.add_page()
        pdf.osszegzo(sorok)
        pdf.tablazat(sorok)
        pdf_bytes = bytes(pdf.output())
    except Exception as e:
        tb = traceback.format_exc()
        print(f"❌ Hiba a dokumentum-import riport PDF készítésekor:\n{tb}")
        # A teljes traceback csak a szerver konzol-naplójában landol (l. fent) - a kliensnek
        # a fájl többi hibaágához hasonlóan (E-DOK-01/02/03) csak egy rövid, hibakódos
        # üzenet megy vissza, nyers kivétel-részlet nélkül.
        return JSONResponse(status_code=400, content={"message": hiba_uzenet("Hiba történt a jelentés készítésekor!", "E-DOK-04")})

    return Response(
        content=pdf_bytes,
        media_type="application/pdf",
        headers={"Content-Disposition": f"attachment; filename=dokumentum_import_riport_{helyi_most().strftime('%Y%m%d_%H%M')}.pdf"},
    )
