from datetime import datetime, timezone
from uuid import UUID

from fastapi import HTTPException
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.company import Company
from app.models.kiosk_device import KioskDevice, KioskDeviceStatus
from app.schemas.kiosk import HeartbeatRequest, KioskDeviceCreate, KioskDeviceUpdate


class KioskService:

    # ── Lesende Operationen ───────────────────────────────────────────────────

    async def list_devices(
        self,
        company_id: UUID,
        db: AsyncSession,
        status_filter: KioskDeviceStatus | None = None,
    ) -> list[KioskDevice]:
        query = (
            select(KioskDevice)
            .where(KioskDevice.company_id == company_id)
            .order_by(KioskDevice.created_at.desc())
        )
        if status_filter is not None:
            query = query.where(KioskDevice.status == status_filter)
        result = await db.scalars(query)
        return list(result.all())

    async def get_device(
        self, device_id: UUID, company_id: UUID, db: AsyncSession
    ) -> KioskDevice:
        device = await db.scalar(
            select(KioskDevice).where(
                KioskDevice.id == device_id,
                KioskDevice.company_id == company_id,
            )
        )
        if device is None:
            raise HTTPException(status_code=404, detail="Gerät nicht gefunden.")
        return device

    # ── Schreibende Operationen ───────────────────────────────────────────────

    async def create_device(
        self,
        company_id: UUID,
        data: KioskDeviceCreate,
        db: AsyncSession,
    ) -> KioskDevice:
        """
        Gerät anlegen.
        Status = pending wenn kiosk_require_approval, sonst approved.
        """
        company = await db.get(Company, company_id)
        require_approval = company.kiosk_require_approval if company else True

        device = KioskDevice(
            company_id=company_id,
            name=data.name,
            location=data.location,
            public_key=data.public_key,
            ip_whitelist=data.ip_whitelist,
            key_algorithm="ed25519",
            status=KioskDeviceStatus.PENDING if require_approval else KioskDeviceStatus.APPROVED,
        )
        db.add(device)
        await db.flush()
        return device

    async def update_device(
        self,
        device_id: UUID,
        company_id: UUID,
        data: KioskDeviceUpdate,
        db: AsyncSession,
    ) -> KioskDevice:
        device = await self.get_device(device_id, company_id, db)
        changes = data.model_dump(exclude_none=True)
        for field, value in changes.items():
            setattr(device, field, value)
        return device

    async def delete_device(
        self, device_id: UUID, company_id: UUID, db: AsyncSession
    ) -> None:
        device = await self.get_device(device_id, company_id, db)
        await db.delete(device)

    # ── Status-Verwaltung ─────────────────────────────────────────────────────

    async def approve_device(
        self, device_id: UUID, company_id: UUID, db: AsyncSession
    ) -> KioskDevice:
        """Gerät freigeben: Status → approved."""
        device = await self.get_device(device_id, company_id, db)
        device.status = KioskDeviceStatus.APPROVED
        return device

    async def revoke_device(
        self, device_id: UUID, company_id: UUID, db: AsyncSession
    ) -> KioskDevice:
        """Gerät sperren: Status → revoked."""
        device = await self.get_device(device_id, company_id, db)
        device.status = KioskDeviceStatus.REVOKED
        return device

    # ── Heartbeat ─────────────────────────────────────────────────────────────

    async def process_heartbeat(
        self,
        device: KioskDevice,
        data: HeartbeatRequest,
        company: Company,
        db: AsyncSession,
    ) -> None:
        """
        Heartbeat-Daten vom Kiosk-Gerät verarbeiten.

        last_heartbeat_at wird hier gesetzt (H-7 Fix: nicht in verify_kiosk_request,
        da der Timestamp erst nach erfolgreicher Route-Logik committed werden soll).
        """
        # Heartbeat-Timestamp erst jetzt setzen – nach erfolgreicher Auth + Route-Logik
        device.last_heartbeat_at = datetime.now(timezone.utc)

        if data.client_version is not None:
            device.client_version = data.client_version

        device.offline_queue_size = data.queued_offline_entries

        if data.current_user_id is not None and company.kiosk_track_current_user:
            device.current_user_id = data.current_user_id
        elif not company.kiosk_track_current_user:
            # DSGVO-Opt-Out: aktuellen User nicht speichern
            device.current_user_id = None

        await db.flush()


kiosk_service = KioskService()
