from fastapi import APIRouter, Request, Depends, Form, status
from fastapi.responses import HTMLResponse, RedirectResponse
from sqlalchemy.orm import Session

import models
from database import get_db
from config import ACCESS_TOKEN_EXPIRE_MINUTES
from security import (
    verify_password, create_access_token, belepesi_token, munkamenet_megszuntetese, HIBAKOD_SZIMULTAN, get_current_user, get_current_user_optional,
    is_company_accessible, get_client_ip, biztonsagos_atiranyitas, DUMMY_PASSWORD_HASH,
    create_mfa_pending_token, MFA_PENDING_COOKIE, MFA_PENDING_PERC, MFA_BEALLITAS_PERC,
)
from hibakodok import hiba_uzenet
import login_vedelem
import jelszo_hazirend
import mfa_utils
from templates_env import templates

router = APIRouter()


@router.get("/login", response_class=HTMLResponse)
def login_page(request: Request, aktivalva: int = 0, hibakod: str = ""):
    # ?aktivalva=1: az aktiváló linken sikeresen beállított jelszó után ide irányítunk (routers/aktivalas.py)
    context = {"info": "A fiókod aktiválva lett. Most már bejelentkezhetsz az új jelszavaddal."} if aktivalva else {}
    # ?hibakod=E-LIC-01: a security.get_current_user() ide irányít, ha a fiókba közben máshol is beléptek (v33). A felhasználó NEM
    # kapja meg a tényleges okot, csak egy rövid üzenetet és a hibakódot (a magyarázat a rendszeradmin felületen, a Rendszerinformáció
    # oldalon van). Csak ez az egy, előre ismert kód jelenik meg - a kérésből érkező szöveg a lapra soha nem kerül.
    if hibakod == HIBAKOD_SZIMULTAN:
        context["error"] = hiba_uzenet("A munkamenet megszűnt, kérjük, jelentkezz be újra!", HIBAKOD_SZIMULTAN)
    return templates.TemplateResponse(
        request=request,
        name="login.html",
        context=context
    )


@router.post("/login")
def login_post(
    request: Request,
    email: str = Form(...),
    password: str = Form(...),
    db: Session = Depends(get_db)
):
    client_ip = get_client_ip(request)

    # Brute-force védelem (2026-10-04-től adatbázis-alapú, minden workerre érvényes tiltással, a
    # rendszeradmin értesítésével és kézi feloldással) - a szabályokat l. login_vedelem.py és
    # docs/LOGIN_VEDELEM.md. FONTOS: a tiltott fiók / IP válasza minden esetben ugyanaz a szöveg,
    # és a "nem létező fiók" ugyanúgy viselkedik, mint a létező - így a válaszból nem derül ki,
    # hogy egy e-mail cím regisztrált-e.
    if login_vedelem.tiltva_e(db, client_ip, email):
        return templates.TemplateResponse(
            request=request,
            name="login.html",
            context={"error": login_vedelem.TILTOTT_UZENET}
        )

    user = db.query(models.User).filter(models.User.email == email).first()

    if not user:
        # Időzítés-alapú user-enumeráció ellen: akkor is lefuttatjuk a (fix, nem létező
        # felhasználóhoz tartozó) bcrypt-ellenőrzést, ha az e-mail cím nem is létezik - így a
        # válaszidő nem árulja el, hogy a cím regisztrált-e.
        verify_password(password, DUMMY_PASSWORD_HASH)
        login_vedelem.sikertelen_rogzitese(db, client_ip, email)
        return templates.TemplateResponse(
            request=request,
            name="login.html",
            context={"error": login_vedelem.GENERIKUS_HIBA}
        )

    jelszo_helyes = verify_password(password, user.hashed_password)
    if not jelszo_helyes:
        login_vedelem.sikertelen_rogzitese(db, client_ip, email, user)
        return templates.TemplateResponse(
            request=request,
            name="login.html",
            context={"error": login_vedelem.GENERIKUS_HIBA}
        )

    if user.aktivalva is False:
        # Aktiválásra váró fiók (l. aktivalas_utils.py). Csak helyes jelszó UTÁN jelenik meg (az új fiók jelszavát
        # senki nem ismeri, ezért ide csak akkor jut el valaki, ha a rendszeradmin/cégadmin jelszót állított neki).
        return templates.TemplateResponse(
            request=request,
            name="login.html",
            context={"error": "A fiókod még nincs aktiválva. Nyisd meg az e-mailben kapott aktiváló linket, vagy kérj újat a rendszergazdától!"}
        )

    if not user.aktiv:
        return templates.TemplateResponse(
            request=request,
            name="login.html",
            context={"error": "Ez a felhasználói fiók inaktiválva lett. Kérjük, forduljon a rendszergazdához!"}
        )

    # KÉTLÉPCSŐS HITELESÍTÉS (2026-10-04, 2FA 2. fázis - docs/KETLEPCSOS_HITELESITES.md): ha a felhasználóra
    # kötelező (bármelyik cége kapcsolja be - l. mfa_utils.szukseges_e), a jelszó még NEM elég a belépéshez.
    #   - kész beállítás: átmeneti (mfa_pending) tokent adunk, és a /mfa oldalon kérjük a kódot;
    #   - nincs még beállítása ("beallitando"): átmeneti tokent adunk és a /mfa/beallitas QR-oldalra irányítjuk
    #     (3. fázis); a belépés csak a beállítás első érvényes kódjának beírása után történik meg;
    #   - a beállítás volt megerősített, de nem olvasható ("helyreallitando", pl. megváltozott kulcs): a belépést NEM
    #     engedjük, és csak jelszóval újra be sem lehet állítani (különben egy kulcscsere kikapcsolná a 2FA-t) -
    #     rendszergazdai jelszó-visszaállítás kell (5. fázis: az /admin-jelszo-csere törli a beállítást is) vagy a mfa_felold.py.
    # SZÁNDÉKOSAN nem hívjuk itt a login_vedelem.sikeres_torlese()-t: a számlálókat csak a KÓD sikeres beírása
    # nullázza (a /mfa végpont), különben a jelszó ismerője a jelszó újraküldésével kinullázhatná a kód
    # találgatásának számlálóját.
    mfa_allapot = mfa_utils.allapot(db, user)
    if mfa_allapot == mfa_utils.ALLAPOT_KESZ:
        resp = RedirectResponse(url="/mfa", status_code=status.HTTP_303_SEE_OTHER)
        resp.set_cookie(
            key=MFA_PENDING_COOKIE,
            value=create_mfa_pending_token(user.email),
            httponly=True,
            secure=True,
            samesite="strict",
            path="/mfa",                       # csak a /mfa végpontok kapják meg
            max_age=MFA_PENDING_PERC * 60
        )
        return resp
    if mfa_allapot == mfa_utils.ALLAPOT_BEALLITANDO:
        resp = RedirectResponse(url="/mfa/beallitas", status_code=status.HTTP_303_SEE_OTHER)
        resp.set_cookie(
            key=MFA_PENDING_COOKIE,
            value=create_mfa_pending_token(user.email, cel="beallitas"),
            httponly=True,
            secure=True,
            samesite="strict",
            path="/mfa",
            max_age=MFA_BEALLITAS_PERC * 60
        )
        return resp
    if mfa_allapot != mfa_utils.ALLAPOT_KI:
        db.add(models.AuditLog(
            company_id=user.company_id,
            user_id=user.id,
            field_name="Bejelentkezés",
            old_value="-",
            new_value=f"Elutasítva: a kétlépcsős hitelesítés beállítása nem olvasható vissza, rendszeradmin-reset kell ({client_ip})"
        ))
        db.commit()
        return templates.TemplateResponse(
            request=request,
            name="login.html",
            context={"error": "A kétlépcsős hitelesítésed beállítása nem olvasható vissza (pl. a szerver titkosítási kulcsa megváltozott), "
                              "ezért újra be kell állítani. Kérd a rendszergazda segítségét!"}
        )

    login_vedelem.sikeres_torlese(db, client_ip, email)

    # ÚJ (2026-10-02-i user-kérés): a Jogosultsági napló (routers/settings.py, "Jogosultsági
    # napló" tab) eddig csak cég-/felhasználó-aktiválásokat tartalmazott - a user kérte, hogy a
    # be- és kijelentkezéseket is naplózzuk, továbbra is kizárólag jogosultsági jellegű
    # eseményként (nem a gépek eseménytörténetéhez kötve).
    db.add(models.AuditLog(
        company_id=user.company_id,
        user_id=user.id,
        field_name="Bejelentkezés",
        old_value="-",
        new_value=f"Sikeres bejelentkezés ({client_ip})"
    ))
    db.commit()

    # v32: a belépés új munkamenet-azonosítót (sid) indít: ez dobja ki a fiók esetleges másik, még élő munkamenetét
    # (rendszeradminisztrátornak nincs sid-je, és ő több helyen is bejelentkezve lehet) - l. security.belepesi_token.
    access_token = belepesi_token(db, user, client_ip)

    # v36: ha a jelszó a következő napokban lejár, a belépés a Profilom jelszó-kártyájára visz (jelszo_hazirend.belepes_utani_cel)
    resp = RedirectResponse(url=jelszo_hazirend.belepes_utani_cel(db, user), status_code=status.HTTP_303_SEE_OTHER)
    resp.set_cookie(
        key="access_token",
        value=access_token,
        httponly=True,
        secure=True,
        samesite="lax",
        max_age=ACCESS_TOKEN_EXPIRE_MINUTES * 60
    )

    return resp


@router.post("/logout")
def logout(request: Request, db: Session = Depends(get_db)):
    # SZÁNDÉKOSAN POST (2026-10-03-i biztonsági audit, 4. fázis - konzisztencia a
    # valaszt_ceget()-nél korábban javított ugyanilyen hibával): egy állapotot módosító GET
    # végpont (AuditLog bejegyzés + cookie-törlés) a SameSite=Lax cookie miatt egy top-level
    # navigációval (pl. egy külső oldalon <img src="/logout">) is kiváltható lett volna. A
    # kliens oldali hívók (static/js/common.js logoutUser(), l. ott) egy dinamikusan
    # összeállított formmal küldik ezt mostantól.
    #
    # get_current_user_optional-lel (nem get_current_user-rel) hívjuk, mert a kijelentkezésnek
    # akkor is működnie kell, ha a token már lejárt/érvénytelen - ilyenkor egyszerűen nincs
    # mit naplózni, de a cookie-k törlése és a /login-ra irányítás továbbra is megtörténik.
    current_user = get_current_user_optional(request, db)
    if current_user:
        munkamenet_megszuntetese(db, current_user)       # v32: kilépés után a token (még ha át is másolták) sem használható
        db.add(models.AuditLog(
            company_id=current_user.company_id,
            user_id=current_user.id,
            field_name="Kijelentkezés",
            old_value="-",
            new_value=f"Kijelentkezett ({get_client_ip(request)})"
        ))
        db.commit()

    resp = RedirectResponse(url="/login", status_code=status.HTTP_303_SEE_OTHER)
    resp.delete_cookie("access_token")
    resp.delete_cookie("workspace_id")  # <-- Ezt add hozzá!
    resp.delete_cookie(MFA_PENDING_COOKIE, path="/mfa")   # egy félbehagyott 2FA-belépés átmeneti tokenje is megszűnik
    return resp


@router.post("/valaszt-ceget/{company_id}")
def valaszt_ceget(
    request: Request,
    company_id: int,
    next: str = Form("/"),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    """Beállítja a kiválasztott cég azonosítóját egy biztonságos Cookie-ba, majd visszairányít.
    A választást a felhasználó fiókjában is tartósan elmentjük (utolso_ceg_id) - így a cookie
    lejárta, kijelentkezés utáni új bejelentkezés, vagy másik eszköz esetén is ez lesz az
    alapértelmezett Munkakörnyezet, nem mindig a felhasználó saját (company_id) cége.

    SZÁNDÉKOSAN POST (2026-09-24-i külső audit találata - korábban GET volt): egy állapotot
    változtató műveletnek nem szabadna GET-nek lennie, mert a SameSite=Lax cookie egy top-level
    navigációnál (pl. egy phishing linkre kattintva) is elküldésre kerül - egy GET-es
    munkakörnyezet-váltó URL-t így egy külső oldalról is ki lehetett volna váltani."""
    if not is_company_accessible(current_user, company_id):
        return RedirectResponse(url="/", status_code=303)

    current_user.utolso_ceg_id = company_id
    db.commit()

    # Visszairányítás a tiszta URL-re (Query paraméterek nélkül)
    # Nyílt átirányítás ellen: csak a saját oldalon belüli abszolút útvonal mehet át (security.biztonsagos_atiranyitas).
    tiszta_url = biztonsagos_atiranyitas(next)
    response = RedirectResponse(url=tiszta_url, status_code=303)

    # Cookie beállítása (1 napig érvényes, JavaScriptből nem olvasható = biztonságos)
    response.set_cookie(key="workspace_id", value=str(company_id), httponly=True, secure=True, samesite="lax", max_age=86400)
    return response
