"""DSGVO Art. 15 (Auskunftsrecht): strukturierter Export aller personenbezogenen
Daten eines Users. Rein lesend, keine Schreiboperation. Ergänzt die bereits
vorhandene Art.-17-Anonymisierung (user_service.anonymize_user)."""
import json
from datetime import date, datetime, time
from decimal import Decimal
from uuid import UUID

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

from app.models.absence import Absence
from app.models.absence_comment import AbsenceComment
from app.models.audit_log import AuditLog
from app.models.hours_payout import HoursPayout
from app.models.overtime_balance import OvertimeBalance
from app.models.time_entry import TimeEntry
from app.models.user import User
from app.models.vacation_balance import VacationBalance


def _json_default(value):
    if isinstance(value, (datetime, date, time)):
        return value.isoformat()
    if isinstance(value, Decimal):
        return float(value)
    if isinstance(value, UUID):
        return str(value)
    raise TypeError(f"Nicht serialisierbar: {type(value)}")


def _row(obj, fields: list[str]) -> dict:
    return {f: getattr(obj, f) for f in fields}


class GdprService:

    async def export_user_data(self, user_id: UUID, company_id: UUID, db: AsyncSession) -> dict:
        """Alle personenbezogenen Daten eines Users, company-isoliert. Wirft
        ValueError wenn der User nicht in der Firma existiert (404 im Router)."""
        user = await db.get(User, user_id)
        if user is None or user.company_id != company_id:
            raise ValueError("Mitarbeiter nicht gefunden")

        profile = _row(user, [
            "id", "first_name", "last_name", "email", "role", "personnel_number",
            "is_active", "entry_date", "exit_date", "created_at",
        ])

        time_entries = [
            _row(e, [
                "id", "date", "start_time", "end_time", "break_minutes",
                "worked_hours", "status", "source", "note", "created_at",
            ])
            for e in (await db.scalars(
                select(TimeEntry).where(TimeEntry.user_id == user_id).order_by(TimeEntry.date)
            )).all()
        ]

        absences = [
            _row(a, [
                "id", "type_id", "start_date", "end_date", "half_day_start",
                "half_day_end", "working_days", "status", "note",
                "rejection_reason", "substitute_id", "created_at",
            ])
            for a in (await db.scalars(
                select(Absence).where(Absence.user_id == user_id).order_by(Absence.start_date)
            )).all()
        ]

        absence_comments = [
            _row(c, ["id", "absence_id", "body", "is_system", "created_at"])
            for c in (await db.scalars(
                select(AbsenceComment).where(AbsenceComment.author_id == user_id)
            )).all()
        ]

        vacation_balances = [
            _row(v, ["id", "year", "entitled_days", "special_days", "carried_over", "used_days"])
            for v in (await db.scalars(
                select(VacationBalance).where(VacationBalance.user_id == user_id).order_by(VacationBalance.year)
            )).all()
        ]

        overtime_balance = await db.scalar(
            select(OvertimeBalance).where(OvertimeBalance.user_id == user_id)
        )
        overtime = _row(overtime_balance, ["total_hours", "taken_hours"]) if overtime_balance else None

        hours_payouts = [
            _row(p, [
                "id", "hours", "period_year", "period_month", "note", "status",
                "rejection_reason", "decided_at", "created_at",
            ])
            for p in (await db.scalars(
                select(HoursPayout).where(HoursPayout.user_id == user_id).order_by(HoursPayout.created_at)
            )).all()
        ]

        # AuditLog: nur Einträge über die Person, keine, bei denen sie nur Akteur war
        # (letzteres wäre kein Datum ÜBER die Person, sondern über deren Handlung an Dritten)
        audit_entries = [
            _row(a, ["id", "action", "entity_type", "entity_id", "created_at"])
            for a in (await db.scalars(
                select(AuditLog).where(
                    AuditLog.entity_type == "user", AuditLog.entity_id == user_id
                ).order_by(AuditLog.created_at)
            )).all()
        ]

        return {
            "export_created_at": datetime.utcnow(),
            "profile": profile,
            "time_entries": time_entries,
            "absences": absences,
            "absence_comments": absence_comments,
            "vacation_balances": vacation_balances,
            "overtime_balance": overtime,
            "hours_payouts": hours_payouts,
            "audit_log_about_user": audit_entries,
        }

    def to_json(self, data: dict) -> str:
        return json.dumps(data, default=_json_default, indent=2, ensure_ascii=False)


gdpr_service = GdprService()
