import os
from datetime import datetime, timezone

from fastapi import APIRouter, Request, Depends, Form, File, UploadFile
from fastapi.responses import HTMLResponse, Response, JSONResponse
from sqlalchemy.orm import Session

import models
from security import EMELOGEP_SZAKMAI_SZEREPKOROK
from database import get_db
from security import (
    is_safe_extension, sanitize_filename, get_current_user, get_working_company_id,
    apply_user_data_scope_filter, is_machine_allowed, is_company_active, require_modul
)
from image_utils import tomorit_kepet
from templates_env import templates

router = APIRouter()

# --- SZERVIZ WORKER (Service Worker): a napi ellenőrzés / gép-adatlap / új gép felvitel
# jelerősség/net nélküli eléréséhez. Gyökér (/) útvonalon kiszolgálva, hogy a regisztrációja
# az egész oldalra kiterjedő (root scope) legyen. ---
SW_JS = r"""
const CACHE_NEV = 'emeloreg-offline-v7';
// v7 (v94): a favicon tartalma megváltozott (négyzetes, csak az embléma - felirat nélkül), a régi gyorsítótárazott példány ne maradjon meg.
// v6 (v85): a Defensys logó helyett az EMELOG Database logó (emelog_logo_w.svg / emelog_logo.svg / emelog_favicon.svg) van az előre gyorsítótárazott listában.
// v49: a service worker által kiszolgált oldalak (gép-kereső, mobil csekklista, új gép offline) a KÖZÖS designt használják (_head_kozos.html), ezért a közös
// stílus / téma-szkript / fejléc-szkriptek is előre gyorsítótárazódnak - különben net nélkül a héj stílus nélkül jelenne meg. Ha ezt a listát vagy a
// közös head-et módosítod, emeld a CACHE_NEV verzióját (a régi gyorsítótár az activate-nél törlődik). v4 (v51): a közös head viewport / theme-color meta-ja és az okostelefonos CSS miatt. v5 (v55): a precache-elt héjak (mobil csekklista, új gép offline) űrlapmezői inputmode / autocomplete attribútumokat kaptak.
const ELORE_CACHELT_URLAK = [
    '/mobil-csekklista-shell',
    '/uj-gep-offline-shell',
    '/gep-kereses-shell',
    '/static/emelog_logo_w.svg',
    '/static/emelog_logo.svg',
    '/static/emelog_favicon.svg',
    '/static/css/emelog.css',
    '/static/css/jetbrains-mono.css',
    '/static/fonts/jetbrains-mono/jetbrains-mono-latin-400-normal.woff2',
    '/static/fonts/jetbrains-mono/jetbrains-mono-latin-500-normal.woff2',
    '/static/fonts/jetbrains-mono/jetbrains-mono-latin-700-normal.woff2',
    '/static/js/tema.js',
    '/static/js/common.js',
    '/static/js/pages/gep-kereses.js',
    '/static/js/pages/mobil-csekklista.js',
    '/static/js/pages/uj-gep-offline.js',
    'https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css',
    'https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js',
    'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css'
];

self.addEventListener('install', event => {
    self.skipWaiting();
    event.waitUntil(
        caches.open(CACHE_NEV).then(async cache => {
            for (const url of ELORE_CACHELT_URLAK) {
                try {
                    await cache.add(url);
                } catch (e) {
                    console.warn('Nem sikerult elore-gyorsitotarazni:', url, e);
                }
            }
        })
    );
});

self.addEventListener('activate', event => {
    // A régi verziójú gyorsítótárak törlése (v49: emeloreg-offline-v2 -> v3), majd az oldalak átvétele.
    event.waitUntil(
        caches.keys()
            .then(nevek => Promise.all(nevek.filter(n => n.startsWith('emeloreg-offline-') && n !== CACHE_NEV).map(n => caches.delete(n))))
            .then(() => self.clients.claim())
    );
});

self.addEventListener('fetch', event => {
    const req = event.request;
    let url;
    try {
        url = new URL(req.url);
    } catch (e) {
        return;
    }

    // Meglevo gep oldalak (QR / napi ellenorzes / adatlap): halozat elsobbseg, offline eseten
    // a helyi, gep-fuggetlen hejjal valaszolunk - a kliens JS a szerepkor alapjan donti el, mit mutat.
    if (req.mode === 'navigate' && (
        url.pathname.startsWith('/q/') ||
        url.pathname.startsWith('/mobil-csekklista/') ||
        url.pathname.startsWith('/szerkesztes/')
    )) {
        event.respondWith(
            fetch(req).catch(() => caches.match('/mobil-csekklista-shell'))
        );
        return;
    }

    // Uj gep felvitele: kulon, gep-fuggetlen hej.
    if (req.mode === 'navigate' && url.pathname === '/uj-gep') {
        event.respondWith(
            fetch(req).catch(() => caches.match('/uj-gep-offline-shell'))
        );
        return;
    }

    // Azonosito-alapu gepkereses (gyari/leltari szam): kulon, gep-fuggetlen hej.
    if (req.mode === 'navigate' && (url.pathname === '/gep-kereses' || url.pathname === '/gep-kereses-eredmeny')) {
        event.respondWith(
            fetch(req).catch(() => caches.match('/gep-kereses-shell'))
        );
        return;
    }

    // Sajat statikus allomanyok (css / js / svg / betutipus) es az elore ismert CDN-ek: azonnal a gyorsitotarbol (gyors, net nelkul is mukodik),
    // a hatterben pedig frissitjuk (v49: korabban "cache-first" volt, ami az emelog.css modositasa utan orokre a regi stilust adta volna).
    const sajatStatikus = url.origin === self.location.origin && /^\/static\/.+\.(css|js|svg|woff2?)$/.test(url.pathname);
    const ismertCdn = ELORE_CACHELT_URLAK.includes(req.url);
    if (req.method === 'GET' && (sajatStatikus || ismertCdn)) {
        const halozati = caches.open(CACHE_NEV).then(cache =>
            fetch(req).then(resp => {
                if (resp && resp.ok) cache.put(req, resp.clone());
                return resp;
            })
        ).catch(() => null);
        event.waitUntil(halozati);
        event.respondWith(
            caches.match(req).then(cachedResp => cachedResp || halozati.then(resp => resp || Response.error()))
        );
    }
    // Minden mas keres (API hivasok, POST-ok) a bongeszo alapertelmezett halozati viselkedeset kapja.
});
"""


@router.get("/sw.js")
def service_worker():
    return Response(content=SW_JS, media_type="application/javascript", headers={"Cache-Control": "no-cache"})


@router.get("/mobil-csekklista-shell", response_class=HTMLResponse)
def mobil_csekklista_shell(request: Request):
    """
    Gép-független, statikusan előre-gyorsítótárazható héj. A Service Worker ezt szolgálja ki
    bármely /q/{uuid}, /mobil-csekklista/{uuid} vagy /szerkesztes/{uuid} kérésre, ha nincs hálózat -
    a kliens oldali JS a helyi (IndexedDB) gyorsítótárból tölti be a gép adatait, és a mentett
    szerepkör alapján dönti el, hogy a napi ellenőrzés űrlapot vagy a gép-ellenőrző nézetet mutatja.
    """
    return templates.TemplateResponse(
        request=request,
        name="mobil-csekklista.html",
        context={"gep": None, "kerdesek": [], "user": None, "szerver_adat_json": "null"}
    )


@router.get("/uj-gep-offline-shell", response_class=HTMLResponse)
def uj_gep_offline_shell(request: Request):
    """
    Önálló, gép-független héj az új gép offline felviteléhez. A hozzá szükséges referenciaadatokat
    (telephelyek, gyártók, típusok, géptípusok, lehetséges fő gépek) a kliens oldali JS a helyi
    (IndexedDB) gyorsítótárból olvassa - ezeket az /offline/szinkronizacio-adatok tölti fel, amíg van net.
    """
    return templates.TemplateResponse(request=request, name="uj-gep-offline.html", context={})


@router.get("/gep-kereses-shell", response_class=HTMLResponse)
def gep_kereses_shell(request: Request):
    """
    Gép-független, előre-gyorsítótárazható héj az azonosító (gyári/leltári szám) alapú kereséshez.
    A Service Worker ezt szolgálja ki a /gep-kereses és /gep-kereses-eredmeny útvonalakra, ha nincs
    hálózat - a kliens oldali JS a helyi (IndexedDB) gyorsítótárból keres, és a talált gép uuid-jára
    navigál (amit a /q/ útvonal offline-fallbackje már ismer).
    """
    return templates.TemplateResponse(
        request=request,
        name="gep-kereses.html",
        context={"user": None, "hiba": "", "talalatok": None}
    )


@router.get("/offline/szinkronizacio-adatok")
def offline_szinkronizacio_adatok(
    request: Request,
    db: Session = Depends(get_db),
    current_user: models.User = Depends(require_modul("EMELOGEP"))
):
    """
    Az offline (jelerősség nélküli) használathoz szükséges adatok letöltése: a cég gépei
    (teljes adatlap-mezőkkel), a napi ellenőrző kérdések, valamint az új gép felviteléhez
    szükséges referenciaadatok (telephelyek, gyártók, típusok, géptípusok).
    """
    working_company_id = get_working_company_id(current_user, request)

    gep_query = db.query(models.Machine).filter(
        models.Machine.company_id == working_company_id,
        models.Machine.statusz.notin_(["Végleg kivonva/Selejtezve", "Eladva", "Bérleti idő lejárt / Visszaadva"])
    )
    gep_query = apply_user_data_scope_filter(gep_query, current_user)
    gepek = gep_query.all()

    def datum_str(d):
        return d.strftime("%Y-%m-%d") if d else None

    def hasznalati_utasitas_url(g):
        fajlok = [f for f in g.fajlok if f.file_type == "Használati utasítás"]
        return max(fajlok, key=lambda f: f.uploaded_at).file_path if fajlok else None

    gepek_adat = [
        {
            "id": g.id,
            "uuid": g.uuid,
            "megnevezes": g.megnevezes,
            "gyarto": g.gyarto,
            "tipus": g.tipus,
            "geptipus": g.geptipus,
            "gyari_szam": g.gyari_szam,
            "leltari_szam": g.leltari_szam,
            "teherbiras": g.teherbiras,
            "gyartas_datum": datum_str(g.gyartas_datum),
            "uzembe_helyezes_datum": datum_str(g.uzembe_helyezes_datum),
            "uzemora": g.uzemora,
            "statusz": g.statusz,
            "site_id": g.site_id,
            "telephely_nev": g.telephely.nev if g.telephely else None,
            "alapadat_megjegyzes": g.alapadat_megjegyzes,
            "fo_gep_id": g.fo_gep_id,
            "company_id": g.company_id,
            "hasznalati_utasitas_url": hasznalati_utasitas_url(g),
        }
        for g in gepek if g.uuid
    ]

    # A teljes (nem gép-specifikus) céges kérdéslista kerül gyorsítótárba - a geptipus_lista taggel
    # együtt, hogy a kliens a ténylegesen megnyitott gép géptípusa alapján tudjon szűrni (offline
    # állapotban egyetlen közös listát tölt le a rendszer minden gépkezelő-géphez).
    kerdesek = db.query(models.DailyCheckQuestion).filter(
        models.DailyCheckQuestion.company_id == working_company_id
    ).all()
    kerdesek_adat = [
        {"id": k.id, "szoveg": k.szoveg, "kovetkezmeny": k.kovetkezmeny, "geptipus_lista": k.geptipus_lista}
        for k in kerdesek
    ]

    sites = db.query(models.Site).filter(models.Site.company_id == working_company_id).all()
    sites_adat = [{"id": s.id, "nev": s.nev} for s in sites]

    gyartok = db.query(models.Dictionary).filter(
        models.Dictionary.company_id == working_company_id,
        models.Dictionary.kategoria == "Gyarto"
    ).all()
    tipusok = db.query(models.Dictionary).filter(
        models.Dictionary.company_id == working_company_id,
        models.Dictionary.kategoria == "Tipus"
    ).all()
    configs = db.query(models.MachineTypeConfig).filter(
        models.MachineTypeConfig.company_id == working_company_id
    ).all()

    return JSONResponse(content={
        "letoltve": datetime.now(timezone.utc).isoformat(),
        "company_id": working_company_id,
        "user_role": current_user.role,
        "gepek": gepek_adat,
        "kerdesek": kerdesek_adat,
        "sites": sites_adat,
        "gyartok": [r.ertek for r in gyartok],
        "tipusok": [r.ertek for r in tipusok],
        "geptipusok": [c.geptipus for c in configs],
    })


@router.post("/offline/eltero-adat-bejelentes")
async def offline_eltero_adat_bejelentes(
    request: Request,
    machine_id: int = Form(...),
    szoveg: str = Form(""),
    fenykep1: UploadFile = File(None),
    fenykep2: UploadFile = File(None),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(require_modul("EMELOGEP"))
):
    """
    Helyszíni (jellemzően offline rögzített, majd szinkronizált) észrevétel egy már felvitt gépről:
    mindig ÚJ, önálló naplóbejegyzésként kerül mentésre - soha nem ír felül semmilyen meglévő mezőt,
    így nincs szükség ütközés-ellenőrzésre az online állapottal szemben.
    """
    working_company_id = get_working_company_id(current_user, request)

    if current_user.role not in EMELOGEP_SZAKMAI_SZEREPKOROK:      # J-25 (v76): pozitív lista
        return JSONResponse(status_code=403, content={"message": "Nincs jogosultságod ehhez a művelethez!"})

    if not is_company_active(working_company_id, db):
        return JSONResponse(status_code=403, content={"message": "Ez a cég inaktív státuszú, nem menthető!"})

    if current_user.csak_olvasas:
        return JSONResponse(status_code=403, content={"message": "Csak megtekintési jogosultsággal rendelkezel!"})

    gep = db.query(models.Machine).filter(
        models.Machine.id == machine_id,
        models.Machine.company_id == working_company_id
    ).first()

    if not gep or not is_machine_allowed(gep, current_user):
        return JSONResponse(status_code=404, content={"message": "A keresett gép nem található!"})

    if not szoveg and not (fenykep1 and fenykep1.filename) and not (fenykep2 and fenykep2.filename):
        return JSONResponse(status_code=400, content={"message": "Legalább egy megjegyzés vagy fotó szükséges!"})

    log = models.AuditLog(
        machine_id=gep.id,
        user_id=current_user.id,
        field_name="Offline ellenőrzési megjegyzés",
        old_value="-",
        new_value=szoveg if szoveg else "(csak fotó csatolva)"
    )
    db.add(log)

    mappa = f"uploads/{working_company_id}/{gep.id}/offline_ellenorzes"
    for kep_file in (fenykep1, fenykep2):
        if kep_file and getattr(kep_file, "filename", ""):
            if not is_safe_extension(kep_file.filename):
                continue
            os.makedirs(mappa, exist_ok=True)
            safe_filename = sanitize_filename(kep_file.filename)
            kep_bytes = tomorit_kepet(await kep_file.read())
            if kep_bytes is None:
                continue
            safe_filename = safe_filename.rsplit(".", 1)[0] + ".jpg"
            file_loc = f"{mappa}/{datetime.now().strftime('%Y%m%d%H%M%S%f')}_{safe_filename}"
            with open(file_loc, "wb+") as f:
                f.write(kep_bytes)
            db.add(models.File(
                machine_id=gep.id,
                file_path="/" + file_loc,
                file_type="Offline ellenőrzés fotó"
            ))

    db.commit()
    return JSONResponse(status_code=200, content={"message": "Sikeresen rögzítve!"})
