import uuid as _uuid
from datetime import datetime, timezone
from uuid import UUID

from fastapi import APIRouter, Body, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.database import get_db
from app.core.dependencies import require_role
from app.core.kiosk_security import verify_kiosk_request
from app.models.company import Company
from app.models.kiosk_device import KioskDevice, KioskDeviceStatus
from app.models.user import User, UserRole
from app.schemas.kiosk import (
    HeartbeatRequest,
    HeartbeatResponse,
    KioskApproveResponse,
    KioskDeviceCreate,
    KioskDeviceOut,
    KioskDeviceUpdate,
)
from app.schemas.kiosk_auth import (
    KioskNfcLoginRequest,
    KioskPinLoginRequest,
    KioskQrLoginRequest,
    KioskSessionResponse,
    KioskStampRequest,
    KioskStampResponse,
    KioskStampStatusRequest,
    KioskStampStatusResponse,
    KioskUserListEntry,
    KioskUserListResponse,
)
from app.schemas.time_entry import StampInRequest
from app.models.time_entry import EntrySource
from app.services.kiosk_auth_service import _display_name, kiosk_auth_service
from app.services.kiosk_service import kiosk_service
from app.services.kiosk_session_service import SESSION_TTL_SECONDS, kiosk_session_service
from app.services.time_service import time_service

router = APIRouter(prefix="/kiosk", tags=["Kiosk"])

_admin_roles = (UserRole.COMPANY_ADMIN, UserRole.SUPER_ADMIN)


# ── Geräteverwaltung (COMPANY_ADMIN) ──────────────────────────────────────────

@router.get("/devices", response_model=list[KioskDeviceOut])
async def list_devices(
    status: str | None = Query(None, description="Filter: pending, approved oder revoked"),
    current_user: User = require_role(*_admin_roles),
    db: AsyncSession = Depends(get_db),
):
    """Alle registrierten Kiosk-Geräte der Firma auflisten (optional nach Status filtern)."""
    status_filter: KioskDeviceStatus | None = None
    if status is not None:
        try:
            status_filter = KioskDeviceStatus(status)
        except ValueError:
            raise HTTPException(
                status_code=400,
                detail=f"Ungültiger Status-Filter '{status}'. Erlaubt: pending, approved, revoked.",
            )
    devices = await kiosk_service.list_devices(current_user.company_id, db, status_filter)
    return [KioskDeviceOut.model_validate(d) for d in devices]


@router.post("/devices", response_model=KioskDeviceOut, status_code=201)
async def create_device(
    data: KioskDeviceCreate,
    current_user: User = require_role(*_admin_roles),
    db: AsyncSession = Depends(get_db),
):
    """Neues Kiosk-Gerät mit Ed25519-Public-Key registrieren."""
    device = await kiosk_service.create_device(current_user.company_id, data, db)
    await db.commit()
    return KioskDeviceOut.model_validate(device)


@router.get("/devices/{device_id}", response_model=KioskDeviceOut)
async def get_device(
    device_id: UUID,
    current_user: User = require_role(*_admin_roles),
    db: AsyncSession = Depends(get_db),
):
    """Einzelnes Kiosk-Gerät abrufen."""
    device = await kiosk_service.get_device(device_id, current_user.company_id, db)
    return KioskDeviceOut.model_validate(device)


@router.patch("/devices/{device_id}", response_model=KioskDeviceOut)
async def update_device(
    device_id: UUID,
    data: KioskDeviceUpdate,
    current_user: User = require_role(*_admin_roles),
    db: AsyncSession = Depends(get_db),
):
    """Name, Standort oder IP-Whitelist eines Kiosk-Geräts aktualisieren."""
    device = await kiosk_service.update_device(device_id, current_user.company_id, data, db)
    await db.commit()
    return KioskDeviceOut.model_validate(device)


@router.delete("/devices/{device_id}", status_code=204)
async def delete_device(
    device_id: UUID,
    current_user: User = require_role(*_admin_roles),
    db: AsyncSession = Depends(get_db),
):
    """Kiosk-Gerät dauerhaft löschen."""
    await kiosk_service.delete_device(device_id, current_user.company_id, db)
    await db.commit()


@router.post("/devices/{device_id}/approve", response_model=KioskApproveResponse)
async def approve_device(
    device_id: UUID,
    current_user: User = require_role(*_admin_roles),
    db: AsyncSession = Depends(get_db),
):
    """Kiosk-Gerät freigeben (Status: pending → approved)."""
    device = await kiosk_service.approve_device(device_id, current_user.company_id, db)
    await db.commit()
    return KioskApproveResponse(
        id=device.id,
        status=device.status.value,
        message=f"Gerät '{device.name}' wurde freigegeben.",
    )


@router.post("/devices/{device_id}/revoke", response_model=KioskApproveResponse)
async def revoke_device(
    device_id: UUID,
    current_user: User = require_role(*_admin_roles),
    db: AsyncSession = Depends(get_db),
):
    """Kiosk-Gerät sperren (Status → revoked). Erfordert Re-Enrollment."""
    device = await kiosk_service.revoke_device(device_id, current_user.company_id, db)
    await db.commit()
    return KioskApproveResponse(
        id=device.id,
        status=device.status.value,
        message=f"Gerät '{device.name}' wurde gesperrt. Re-Enrollment erforderlich.",
    )


# ── Kiosk-User-Auth (signierte Requests via Ed25519) ─────────────────────────


@router.post("/auth/pin", response_model=KioskSessionResponse)
async def kiosk_login_pin(
    data: KioskPinLoginRequest,
    device: KioskDevice = Depends(verify_kiosk_request),
    db: AsyncSession = Depends(get_db),
):
    """PIN-Login: Personalnummer + 4-16-stellige PIN."""
    user, session_token = await kiosk_auth_service.login_pin(
        personnel_number=data.personnel_number,
        pin=data.pin,
        company_id=device.company_id,
        device_id=device.id,
        db=db,
    )
    return KioskSessionResponse(
        session_token=session_token,
        user_id=user.id,
        user_name=_display_name(user),
        expires_in_seconds=SESSION_TTL_SECONDS,
        auth_method="pin",
    )


@router.post("/auth/nfc", response_model=KioskSessionResponse)
async def kiosk_login_nfc(
    data: KioskNfcLoginRequest,
    device: KioskDevice = Depends(verify_kiosk_request),
    db: AsyncSession = Depends(get_db),
):
    """NFC-Login: NFC-UID der Karte."""
    user, session_token = await kiosk_auth_service.login_nfc(
        nfc_uid=data.nfc_uid,
        pin=data.pin,
        company_id=device.company_id,
        device_id=device.id,
        db=db,
    )
    return KioskSessionResponse(
        session_token=session_token,
        user_id=user.id,
        user_name=_display_name(user),
        expires_in_seconds=SESSION_TTL_SECONDS,
        auth_method="nfc",
    )


@router.post("/auth/qr", response_model=KioskSessionResponse)
async def kiosk_login_qr(
    data: KioskQrLoginRequest,
    device: KioskDevice = Depends(verify_kiosk_request),
    db: AsyncSession = Depends(get_db),
):
    """QR-Login: QR-Token (aus Web-App gescannt)."""
    user, session_token = await kiosk_auth_service.login_qr(
        qr_token=data.qr_token,
        company_id=device.company_id,
        device_id=device.id,
        db=db,
    )
    return KioskSessionResponse(
        session_token=session_token,
        user_id=user.id,
        user_name=_display_name(user),
        expires_in_seconds=SESSION_TTL_SECONDS,
        auth_method="qr",
    )


@router.post("/auth/list", response_model=KioskSessionResponse)
async def kiosk_login_list(
    user_id: _uuid.UUID = Body(..., embed=True),
    device: KioskDevice = Depends(verify_kiosk_request),
    db: AsyncSession = Depends(get_db),
):
    """List-Login: User wählt sich aus Mitarbeiterliste aus (kein Passwort)."""
    user, session_token = await kiosk_auth_service.login_list(
        user_id=user_id,
        company_id=device.company_id,
        device_id=device.id,
        db=db,
    )
    return KioskSessionResponse(
        session_token=session_token,
        user_id=user.id,
        user_name=_display_name(user),
        expires_in_seconds=SESSION_TTL_SECONDS,
        auth_method="list",
    )


@router.get("/auth/users", response_model=KioskUserListResponse)
async def kiosk_user_list(
    device: KioskDevice = Depends(verify_kiosk_request),
    db: AsyncSession = Depends(get_db),
):
    """Mitarbeiterliste für Kiosk-Auswahl."""
    users = await kiosk_auth_service.get_user_list(device.company_id, db)
    return KioskUserListResponse(
        users=[
            KioskUserListEntry(id=u.id, display_name=_display_name(u))
            for u in users
        ]
    )


@router.post("/auth/logout")
async def kiosk_logout(
    session_token: str = Body(..., embed=True),
    device: KioskDevice = Depends(verify_kiosk_request),
):
    """Session invalidieren."""
    await kiosk_session_service.invalidate_session(session_token)
    return {"message": "Abgemeldet."}


# ── Stempel-Aktionen (signiertes Gerät + eingeloggte Kiosk-Session) ──────────

async def _user_from_session(
    session_token: str, device: KioskDevice, db: AsyncSession
) -> User:
    """Lädt den User zur Kiosk-Session, prüft dass Session zu diesem Gerät gehört."""
    session = await kiosk_session_service.require_session(session_token)
    if session["device_id"] != str(device.id):
        raise HTTPException(status_code=401, detail="Session gehört zu einem anderen Gerät.")
    user = await db.get(User, _uuid.UUID(session["user_id"]))
    if user is None or not user.is_active or user.company_id != device.company_id:
        raise HTTPException(status_code=401, detail="Mitarbeiter nicht mehr aktiv.")
    return user


@router.post("/stamp/in", response_model=KioskStampResponse)
async def kiosk_stamp_in(
    data: KioskStampRequest,
    device: KioskDevice = Depends(verify_kiosk_request),
    db: AsyncSession = Depends(get_db),
):
    user = await _user_from_session(data.session_token, device, db)
    entry, warnings = await time_service.stamp_in(
        user, StampInRequest(source=EntrySource.KIOSK, note=data.note), db
    )
    await db.commit()
    return KioskStampResponse(entry_id=entry.id, status="stamped_in", warnings=warnings)


@router.post("/stamp/out", response_model=KioskStampResponse)
async def kiosk_stamp_out(
    data: KioskStampRequest,
    device: KioskDevice = Depends(verify_kiosk_request),
    db: AsyncSession = Depends(get_db),
):
    user = await _user_from_session(data.session_token, device, db)
    entry, warnings = await time_service.stamp_out(user, data.note, db)
    await db.commit()
    return KioskStampResponse(entry_id=entry.id, status="stamped_out", warnings=warnings)


@router.post("/stamp/break-start", response_model=KioskStampResponse)
async def kiosk_break_start(
    data: KioskStampRequest,
    device: KioskDevice = Depends(verify_kiosk_request),
    db: AsyncSession = Depends(get_db),
):
    user = await _user_from_session(data.session_token, device, db)
    entry = await time_service.break_start(user, db)
    await db.commit()
    return KioskStampResponse(entry_id=entry.id, status="break_started")


@router.post("/stamp/break-end", response_model=KioskStampResponse)
async def kiosk_break_end(
    data: KioskStampRequest,
    device: KioskDevice = Depends(verify_kiosk_request),
    db: AsyncSession = Depends(get_db),
):
    user = await _user_from_session(data.session_token, device, db)
    entry = await time_service.break_end(user, db)
    await db.commit()
    return KioskStampResponse(entry_id=entry.id, status="break_ended")


@router.post("/stamp/status", response_model=KioskStampStatusResponse)
async def kiosk_stamp_status(
    data: KioskStampStatusRequest,
    device: KioskDevice = Depends(verify_kiosk_request),
    db: AsyncSession = Depends(get_db),
):
    """Aktueller Stempel-Status des eingeloggten Mitarbeiters (für Button-Zustand)."""
    user = await _user_from_session(data.session_token, device, db)
    entries = await time_service.get_today(user, db)
    open_entry = next((e for e in entries if e.end_time is None), None)
    if open_entry is None:
        return KioskStampStatusResponse(stamped_in=False, on_break=False)
    return KioskStampStatusResponse(
        stamped_in=True,
        on_break=open_entry.break_start is not None,
        start_time=open_entry.start_time.isoformat(),
    )


# ── Kiosk-Endpunkte (signierte Requests via Ed25519) ──────────────────────────

@router.post("/heartbeat", response_model=HeartbeatResponse)
async def heartbeat(
    data: HeartbeatRequest,
    device: KioskDevice = Depends(verify_kiosk_request),
    db: AsyncSession = Depends(get_db),
):
    """
    Heartbeat-Endpunkt für Kiosk-Geräte.

    Muss mit Ed25519-Signatur-Headern aufgerufen werden:
      X-Kiosk-Key-Id, X-Kiosk-Timestamp, X-Kiosk-Nonce, X-Kiosk-Signature

    Gibt Server-Zeit und Konfiguration zurück.
    """
    company = await db.get(Company, device.company_id)
    if company is None:
        raise HTTPException(status_code=500, detail="Firma nicht gefunden.")

    await kiosk_service.process_heartbeat(device, data, company, db)
    await db.commit()

    return HeartbeatResponse(
        server_time=datetime.now(timezone.utc).isoformat(),
        heartbeat_interval_sec=company.kiosk_heartbeat_interval_sec,
        device_status=device.status.value,
    )
