from typing import Annotated
from uuid import UUID

from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile
from fastapi.responses import PlainTextResponse, Response
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.database import get_db
from app.core.dependencies import CurrentUser, require_role
from app.models.user import User, UserRole
from app.schemas.auth import MessageResponse
from app.schemas.user import (
    InviteRequest,
    NextPersonnelNumberResponse,
    NotificationPrefsUpdate,
    NotificationTypeOut,
    SetKioskNfcUidRequest,
    SetKioskPinRequest,
    UserImportResult,
    UserImportRowResult,
    UserListResponse,
    UserOut,
    UserUpdate,
)
from app.services import user_import_service
from app.services.gdpr_service import gdpr_service
from app.services.user_service import user_service

router = APIRouter(prefix="/users", tags=["Users"])

_admin_roles = (UserRole.COMPANY_ADMIN, UserRole.SUPER_ADMIN)
_hr_roles = (UserRole.COMPANY_ADMIN, UserRole.SUPER_ADMIN, UserRole.HR, UserRole.MANAGER)


@router.get("/", response_model=UserListResponse)
async def list_users(
    current_user: User = require_role(*_hr_roles),
    skip: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=500),
    active_only: bool = Query(True),
    search: str | None = Query(None, max_length=100),
    db: AsyncSession = Depends(get_db),
):
    total, users = await user_service.list_users(
        current_user.company_id, db, skip, limit, active_only, search,
    )
    return UserListResponse(total=total, items=[UserOut.model_validate(u) for u in users])


@router.post("/invite", response_model=UserOut, status_code=201)
async def invite_user(
    data: InviteRequest,
    current_user: User = require_role(*_admin_roles),
    db: AsyncSession = Depends(get_db),
):
    user = await user_service.invite(data, current_user.company_id, current_user, db)
    return UserOut.model_validate(user)


@router.get("/colleagues")
async def list_colleagues(
    current_user: CurrentUser,
    db: AsyncSession = Depends(get_db),
):
    """Schlanke Kollegenliste (id + Name) für alle Mitarbeiter – z.B. zur
    Vertreter-Auswahl. Nur aktive User der eigenen Firma (RLS-gefenced),
    ohne sensible Felder."""
    from sqlalchemy import select
    rows = await db.scalars(
        select(User)
        .where(
            User.company_id == current_user.company_id,
            User.is_active.is_(True),
            User.id != current_user.id,
        )
        .order_by(User.last_name, User.first_name)
    )
    return [{"id": str(u.id), "full_name": u.full_name} for u in rows.all()]


@router.get("/me", response_model=UserOut)
async def get_me(current_user: CurrentUser):
    return UserOut.model_validate(current_user)


@router.get("/me/notification-prefs", response_model=list[NotificationTypeOut])
async def get_notification_prefs(current_user: CurrentUser):
    from app.core.notifications import NOTIFICATION_TYPES, pref_enabled
    return [
        NotificationTypeOut(key=k, label=label, enabled=pref_enabled(current_user, k))
        for k, (label, _default) in NOTIFICATION_TYPES.items()
    ]


@router.patch("/me/notification-prefs", response_model=list[NotificationTypeOut])
async def update_notification_prefs(
    data: NotificationPrefsUpdate,
    current_user: CurrentUser,
    db: AsyncSession = Depends(get_db),
):
    from app.core.notifications import NOTIFICATION_TYPES, normalize_prefs, pref_enabled
    merged = dict(current_user.notification_prefs or {})
    merged.update(normalize_prefs(data.prefs))
    current_user.notification_prefs = merged  # neues dict → JSONB-Änderung erkannt
    await db.commit()
    return [
        NotificationTypeOut(key=k, label=label, enabled=pref_enabled(current_user, k))
        for k, (label, _default) in NOTIFICATION_TYPES.items()
    ]


@router.get("/next-personnel-number", response_model=NextPersonnelNumberResponse)
async def next_personnel_number(
    current_user: User = require_role(*_hr_roles),
    db: AsyncSession = Depends(get_db),
):
    """Schlägt die nächste freie Personalnummer vor (ohne den Counter zu erhöhen)."""
    suggestion = await user_service.next_personnel_suggestion(current_user.company_id, db)
    return NextPersonnelNumberResponse(next=suggestion)


@router.get("/by-personnel/{number}", response_model=UserOut)
async def get_user_by_personnel(
    number: str,
    current_user: User = require_role(*_hr_roles),
    db: AsyncSession = Depends(get_db),
):
    user = await user_service.get_by_personnel_number(number, current_user.company_id, db)
    return UserOut.model_validate(user)


@router.get("/import-template.csv", response_class=PlainTextResponse)
async def import_template(
    current_user: User = require_role(*_admin_roles),
):
    csv_text = user_import_service.build_template_csv()
    return PlainTextResponse(
        content=csv_text,
        media_type="text/csv",
        headers={"Content-Disposition": 'attachment; filename="user-import-template.csv"'},
    )


_MAX_UPLOAD_BYTES = 10 * 1024 * 1024  # 10 MB


async def _read_upload(file: UploadFile) -> bytes:
    """Liest eine UploadFile mit Größenbegrenzung (max 10 MB)."""
    content = await file.read(_MAX_UPLOAD_BYTES + 1)
    if len(content) > _MAX_UPLOAD_BYTES:
        raise HTTPException(
            status_code=413,
            detail=f"Datei zu groß. Maximale Upload-Größe: {_MAX_UPLOAD_BYTES // (1024 * 1024)} MB.",
        )
    return content


@router.post("/import/preview", response_model=UserImportResult)
async def user_import_preview(
    file: Annotated[UploadFile, File()],
    current_user: User = require_role(*_admin_roles),
    db: AsyncSession = Depends(get_db),
):
    content = await _read_upload(file)
    result = await user_import_service.preview_csv(content, current_user.company_id, current_user, db)
    return _to_import_result_schema(result)


@router.post("/import/apply", response_model=UserImportResult)
async def user_import_apply(
    file: Annotated[UploadFile, File()],
    current_user: User = require_role(*_admin_roles),
    db: AsyncSession = Depends(get_db),
):
    content = await _read_upload(file)
    result = await user_import_service.apply_csv(content, current_user.company_id, current_user, db)
    return _to_import_result_schema(result)


def _to_import_result_schema(result) -> UserImportResult:
    return UserImportResult(
        total_rows=result.total_rows,
        created=result.created,
        reactivated=result.reactivated,
        errors=result.errors,
        items=[
            UserImportRowResult(
                row=i.row, email=i.email, personnel_number=i.personnel_number,
                action=i.action, message=i.message,
            )
            for i in result.items
        ],
    )


@router.get("/{user_id}", response_model=UserOut)
async def get_user(
    user_id: UUID,
    current_user: User = require_role(*_hr_roles),
    db: AsyncSession = Depends(get_db),
):
    user = await user_service.get_by_id(user_id, current_user.company_id, db)
    return UserOut.model_validate(user)


@router.patch("/{user_id}", response_model=UserOut)
async def update_user(
    user_id: UUID,
    data: UserUpdate,
    current_user: User = require_role(*_admin_roles),
    db: AsyncSession = Depends(get_db),
):
    user = await user_service.update(user_id, data, current_user, db)
    return UserOut.model_validate(user)


@router.post("/{user_id}/deactivate", response_model=UserOut)
async def deactivate_user(
    user_id: UUID,
    current_user: User = require_role(*_admin_roles),
    db: AsyncSession = Depends(get_db),
):
    user = await user_service.deactivate(user_id, current_user, db)
    return UserOut.model_validate(user)


@router.post("/{user_id}/reactivate", response_model=UserOut)
async def reactivate_user(
    user_id: UUID,
    current_user: User = require_role(*_admin_roles),
    db: AsyncSession = Depends(get_db),
):
    user = await user_service.reactivate(user_id, current_user, db)
    return UserOut.model_validate(user)


@router.post("/{user_id}/anonymize", response_model=UserOut)
async def anonymize_user(
    user_id: UUID,
    current_user: User = require_role(*_admin_roles),
    db: AsyncSession = Depends(get_db),
):
    """DSGVO Art. 17: Personenbezug des Kontos entfernen (nicht umkehrbar).
    Referenzierte Daten (Zeit/Abwesenheit/Auszahlung) bleiben entkoppelt erhalten."""
    user = await user_service.anonymize_user(user_id, current_user, db)
    return UserOut.model_validate(user)


@router.get("/{user_id}/export-data")
async def export_user_data(
    user_id: UUID,
    current_user: CurrentUser,
    db: AsyncSession = Depends(get_db),
):
    """DSGVO Art. 15 (Auskunftsrecht): strukturierter Export aller
    personenbezogenen Daten des Users als JSON. Eigenes Konto immer erlaubt,
    fremde Konten nur für HR/Admin/Manager der eigenen Firma."""
    if user_id != current_user.id and current_user.role not in _hr_roles:
        raise HTTPException(status_code=403, detail="Keine Berechtigung für dieses Konto")

    try:
        data = await gdpr_service.export_user_data(user_id, current_user.company_id, db)
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))

    content = gdpr_service.to_json(data)
    return Response(
        content=content, media_type="application/json",
        headers={"Content-Disposition": f"attachment; filename=datenauskunft_{user_id}.json"},
    )


@router.post("/{user_id}/kiosk-pin", response_model=MessageResponse)
async def set_kiosk_pin(
    user_id: UUID,
    data: SetKioskPinRequest,
    current_user: CurrentUser,
    db: AsyncSession = Depends(get_db),
):
    # Users can set their own PIN; admins can set for any user in company
    if user_id != current_user.id and not current_user.is_admin_or_above():
        from fastapi import HTTPException
        raise HTTPException(status_code=403, detail="Not allowed")
    user = await user_service.get_by_id(user_id, current_user.company_id, db)
    await user_service.set_kiosk_pin(user, data.pin, db)
    return MessageResponse(message="Kiosk PIN updated")


@router.post("/{user_id}/kiosk-nfc", response_model=MessageResponse)
async def set_kiosk_nfc(
    user_id: UUID,
    data: SetKioskNfcUidRequest,
    current_user: CurrentUser,
    db: AsyncSession = Depends(get_db),
):
    """NFC-Karte einem Mitarbeiter zuordnen. Nur Admin/HR - die Karte allein ist
    kein Geheimnis (klonbare UID), daher darf sie nicht selbst zugewiesen werden."""
    if not current_user.is_admin_or_above():
        raise HTTPException(status_code=403, detail="Not allowed")
    user = await user_service.get_by_id(user_id, current_user.company_id, db)
    await user_service.set_kiosk_nfc_uid(user, data.nfc_uid, db)
    return MessageResponse(message="Kiosk NFC-UID updated")
