"""2FA mentőkódok (v34): belépés mentőkóddal, és a saját készlet kezelése. Leírás: docs/KETLEPCSOS_HITELESITES.md ("Mentőkódok" fejezet).

VÉGPONTOK
    GET  /mfa/mentokod            a belépés mentőkódos változata (átmeneti süti kell, mint a /mfa-hoz; csak megjelenít)
    POST /mfa/mentokod            a mentőkód ellenőrzése: siker -> teljes munkamenet (mint a 6 jegyű kódnál), a kód felhasznált, napló + értesítő levél
    GET  /mfa/mentokodok          a SAJÁT készlet állapota (hány kód maradt) + "új készlet" űrlap (bejelentkezett felhasználó; csak megjelenít)
    POST /mfa/mentokodok/uj       új készlet: a SAJÁT JELSZÓ és egy ÉRVÉNYES HITELESÍTŐ KÓD kell hozzá; a régi készlet érvénytelen; a kódok egyszer látszanak

BIZTONSÁG
    - A belépés ugyanazt a találgatás-védelmet kapja, mint a 6 jegyű kód (login_vedelem: a próbálkozást az ellenőrzés ELŐTT számoljuk; a
      rendszeradminnál időzített, nem végleges tiltás). A hibaüzenet nem árulja el, melyik része volt rossz.
    - Készletet csak olyan felhasználó kérhet, akinek MEGERŐSÍTETT 2FA-ja van, és a kérés a jelszavát ÉS egy friss hitelesítő kódot is igényli -
      egy ellopott munkamenet önmagában nem elég (a támadó nem tudna saját mentőkódot generálni).
    - Az új készlet oldala `Cache-Control: no-store` + `Referrer-Policy: no-referrer`; a kódok nem kerülnek URL-be, naplóba, levélbe.
    - Kérés-eredet védelem (Sec-Fetch-Site) a készlet-kérésen, mint a többi 2FA-művelet.
"""
from fastapi import APIRouter, Request, Depends, Form, status
from fastapi.responses import HTMLResponse, RedirectResponse
from sqlalchemy.orm import Session

import login_vedelem
import mentokod_utils
import mfa_utils
import models
from database import get_db
from routers import mfa as mfa_router
from security import get_client_ip, get_current_user, verify_password
from templates_env import templates

router = APIRouter()

_FEJLECEK = {"Cache-Control": "no-store"}
HIBAS_MENTOKOD_UZENET = "Hibás vagy már felhasznált mentőkód."
HIBAS_JELSZO_UZENET = "Hibás jelszó vagy hitelesítő kód."


# --- Belépés mentőkóddal ------------------------------------------------------------------------
def _belepes_oldal(request: Request, user, hiba: str = None):
    return templates.TemplateResponse(
        request=request, name="mfa_mentokod_belepes.html",
        context={"email": user.email, "hiba": hiba}, headers=_FEJLECEK,
    )


@router.get("/mfa/mentokod", response_class=HTMLResponse)
def mentokod_belepes_oldal(request: Request, db: Session = Depends(get_db)):
    user = mfa_router._felhasznalo_az_atmeneti_tokenbol(request, db)
    if not user:
        return mfa_router._vissza_a_belepeshez()
    return _belepes_oldal(request, user)


@router.post("/mfa/mentokod")
def mentokod_belepes(request: Request, mentokod: str = Form(""), db: Session = Depends(get_db)):
    user = mfa_router._felhasznalo_az_atmeneti_tokenbol(request, db)
    if not user:
        return mfa_router._vissza_a_belepeshez()

    client_ip = get_client_ip(request)
    email = user.email

    if login_vedelem.tiltva_e(db, client_ip, email):
        return _belepes_oldal(request, user, login_vedelem.TILTOTT_UZENET)

    # Ha a szabály időközben megváltozott (kikapcsolták / törölték a beállítást), nem erre az oldalra tartozik (mint a /mfa-nál).
    if mfa_utils.allapot(db, user) != mfa_utils.ALLAPOT_KESZ:
        return mfa_router._vissza_a_belepeshez()

    # A próbálkozást az ELLENŐRZÉS ELŐTT számoljuk (ugyanaz az indok, mint a /mfa-nál: párhuzamos roham ne léphesse át a határt);
    # siker esetén a sikeres_torlese() úgyis törli ennek az e-mailnek a kísérleteit.
    user_id, company_id = user.id, user.company_id
    if login_vedelem.sikertelen_rogzitese(db, client_ip, email, user) is not None:
        return _belepes_oldal(request, user, login_vedelem.TILTOTT_UZENET)

    user = db.get(models.User, user_id)
    if not mentokod_utils.felhasznalas(db, user, mentokod):
        db.rollback()
        return _belepes_oldal(request, db.get(models.User, user_id), HIBAS_MENTOKOD_UZENET)
    db.commit()                                                   # a felhasználás azonnal rögzítve: a kód többé nem használható

    maradt = mentokod_utils.hatralevo(db, user_id)
    login_vedelem.sikeres_torlese(db, client_ip, email)
    db.add(models.AuditLog(
        company_id=company_id, user_id=user_id, field_name="Bejelentkezés", old_value="-",
        new_value=f"Sikeres bejelentkezés mentőkóddal ({client_ip}); felhasználatlan mentőkód: {maradt} db"
    ))
    db.commit()
    mentokod_utils.ertesito_level(db, user_id, client_ip, "hasznalat", maradt)
    return mfa_router._teljes_belepes(db, email, client_ip)


# --- A saját készlet kezelése ---------------------------------------------------------------------
def _kezelo_oldal(request: Request, db: Session, user, hiba: str = None, ok: str = None):
    return mfa_router.mentokod_oldal(
        request, email=user.email, maradt=mentokod_utils.hatralevo(db, user.id), hiba=hiba, ok=ok, tovabb="/valassz-modult",
    )


@router.get("/mfa/mentokodok", response_class=HTMLResponse)
def mentokodok_oldal(request: Request, db: Session = Depends(get_db), current_user: models.User = Depends(get_current_user)):
    """A saját mentőkód-készlet állapota (a kódokat NEM mutatja: azok csak a készítéskor látszanak). Csak megjelenít, nem ír."""
    if not mfa_utils.beallitva_e(current_user):
        return RedirectResponse(url="/valassz-modult", status_code=status.HTTP_303_SEE_OTHER)
    return _kezelo_oldal(request, db, current_user)


@router.post("/mfa/mentokodok/uj")
def mentokodok_uj(
    request: Request,
    jelszo: str = Form(""),
    kod: str = Form(""),
    db: Session = Depends(get_db),
    current_user: models.User = Depends(get_current_user)
):
    """Új mentőkód-készlet a SAJÁT fiókhoz. Kell hozzá a jelszó ÉS egy érvényes hitelesítő kód (utóbbi egyszer használható: ha a felhasználó az
    imént lépett be ugyanazzal, meg kell várnia a következő kódot). A régi készlet (használt és használatlan kódok) érvénytelen lesz."""
    if not mfa_utils.beallitva_e(current_user):
        return RedirectResponse(url="/valassz-modult", status_code=status.HTTP_303_SEE_OTHER)
    if mfa_router._idegen_eredet(request):
        return _kezelo_oldal(request, db, current_user, hiba="A kérés nem erről az oldalról érkezett.")

    client_ip = get_client_ip(request)
    user_id, company_id, email = current_user.id, current_user.company_id, current_user.email

    if login_vedelem.tiltva_e(db, client_ip, email):
        return _kezelo_oldal(request, db, current_user, hiba=login_vedelem.TILTOTT_UZENET)
    # A próbálkozást az ellenőrzés ELŐTT számoljuk (mint a /mfa-nál); siker esetén törlődik.
    if login_vedelem.sikertelen_rogzitese(db, client_ip, email, current_user) is not None:
        return _kezelo_oldal(request, db, db.get(models.User, user_id), hiba=login_vedelem.TILTOTT_UZENET)

    user = db.get(models.User, user_id)
    if not jelszo or not verify_password(jelszo, user.hashed_password):
        db.rollback()
        return _kezelo_oldal(request, db, db.get(models.User, user_id), hiba=HIBAS_JELSZO_UZENET)
    if not mfa_utils.kod_ellenorzese_es_rogzitese(db, user, kod):
        db.rollback()
        return _kezelo_oldal(request, db, db.get(models.User, user_id), hiba=HIBAS_JELSZO_UZENET + " " + "(A telefonod ideje legyen pontos; ha az előbb léptél be ugyanazzal a kóddal, várd meg a következőt.)")

    # Siker: az elfogadott időlépés + az új készlet + a napló EGY tranzakcióban.
    kodok = mentokod_utils.uj_keszlet(db, user_id)
    db.add(models.AuditLog(
        company_id=company_id, user_id=user_id, field_name="Kétlépcsős hitelesítés - mentőkódok", old_value="-",
        new_value=f"Új mentőkód-készlet kérve ({mentokod_utils.DARAB} db, a korábbiak érvénytelenek) ({client_ip})"
    ))
    db.commit()
    login_vedelem.sikeres_torlese(db, client_ip, email)
    mentokod_utils.ertesito_level(db, user_id, client_ip, "generalas", len(kodok))
    return mfa_router.mentokod_oldal(
        request, email=email, kodok=kodok, maradt=len(kodok), kod_szoveg=mfa_router.mentokod_szoveg(email, kodok),
    )
