import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
import { and, eq, inArray, isNull } from "drizzle-orm";
import multipart from "@fastify/multipart";
import { GetObjectCommand } from "@aws-sdk/client-s3";

import {
    authTenantUsers,
    authProfiles,
    customers,
    authRoles,
    authUserRoles,
    authUsers,
    filetags,
    folders,
    tenantExportJobs,
    tenants,
} from "../../db/schema";
import { generateRandomPassword, hashPassword } from "../utils/password";
import { sendMail } from "../utils/mailer";
import { ensureTenantBaseData } from "../modules/bootstrap.service";
import {
    createTenantFullExportArchive,
    importTenantFullExport,
    importTenantFullExportArchive,
} from "../utils/tenantFullExport";
import type { TenantFullExport } from "../utils/tenantFullExport";
import { buildSystemStatus } from "../modules/system-status.service";
import { matrixService } from "../modules/matrix.service";
import { s3 } from "../utils/s3";
import { secrets } from "../utils/secrets";

export default async function adminRoutes(server: FastifyInstance) {
    await server.register(multipart, {
        limits: { fileSize: 1024 * 1024 * 1024 }
    });

    const deriveNameFromEmail = (email: string) => {
        const localPart = email.split("@")[0] || "Benutzer";
        const normalized = localPart.replace(/[._-]+/g, " ").trim();
        const parts = normalized.split(/\s+/).filter(Boolean);
        const firstName = parts[0]
            ? parts[0].charAt(0).toUpperCase() + parts[0].slice(1)
            : "Neuer";
        const lastName = parts.length > 1
            ? parts.slice(1).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ")
            : "Benutzer";

        return { first_name: firstName, last_name: lastName };
    };

    const createTenantSeeds = async (tenantId: number, createdBy: string) => {
        const currentYear = new Date().getFullYear();
        const timestamp = new Date();

        const insertedTags = await server.db
            .insert(filetags)
            .values([
                {
                    tenant: tenantId,
                    name: "Rechnungen",
                    color: "#16a34a",
                    createdDocumentType: "invoices",
                    isSystemUsed: true,
                },
                {
                    tenant: tenantId,
                    name: "Angebote",
                    color: "#2563eb",
                    createdDocumentType: "quotes",
                    isSystemUsed: true,
                },
                {
                    tenant: tenantId,
                    name: "Auftragsbestätigungen",
                    color: "#7c3aed",
                    createdDocumentType: "confirmationOrders",
                    isSystemUsed: true,
                },
                {
                    tenant: tenantId,
                    name: "Lieferscheine",
                    color: "#ea580c",
                    createdDocumentType: "deliveryNotes",
                    isSystemUsed: true,
                },
                {
                    tenant: tenantId,
                    name: "Eingangsrechnungen",
                    color: "#dc2626",
                    incomingDocumentType: "invoices",
                    isSystemUsed: true,
                },
                {
                    tenant: tenantId,
                    name: "Mahnungen",
                    color: "#b91c1c",
                    incomingDocumentType: "reminders",
                    isSystemUsed: true,
                },
            ])
            .returning({
                id: filetags.id,
                name: filetags.name,
                createdDocumentType: filetags.createdDocumentType,
                incomingDocumentType: filetags.incomingDocumentType,
            });

        const invoiceTag = insertedTags.find((tag) => tag.createdDocumentType === "invoices");
        const quoteTag = insertedTags.find((tag) => tag.createdDocumentType === "quotes");
        const confirmationTag = insertedTags.find((tag) => tag.createdDocumentType === "confirmationOrders");
        const deliveryTag = insertedTags.find((tag) => tag.createdDocumentType === "deliveryNotes");
        const incomingInvoiceTag = insertedTags.find((tag) => tag.incomingDocumentType === "invoices");

        const insertedFolders = await server.db
            .insert(folders)
            .values([
                {
                    tenant: tenantId,
                    name: "Ausgangsrechnungen",
                    function: "yearSubCategory",
                    icon: "i-heroicons-document-text",
                    isSystemUsed: true,
                    updatedAt: timestamp,
                    updatedBy: createdBy,
                },
                {
                    tenant: tenantId,
                    name: "Angebote",
                    function: "yearSubCategory",
                    icon: "i-heroicons-document-duplicate",
                    isSystemUsed: true,
                    updatedAt: timestamp,
                    updatedBy: createdBy,
                },
                {
                    tenant: tenantId,
                    name: "Auftragsbestätigungen",
                    function: "yearSubCategory",
                    icon: "i-heroicons-clipboard-document-check",
                    isSystemUsed: true,
                    updatedAt: timestamp,
                    updatedBy: createdBy,
                },
                {
                    tenant: tenantId,
                    name: "Lieferscheine",
                    function: "yearSubCategory",
                    icon: "i-heroicons-truck",
                    isSystemUsed: true,
                    updatedAt: timestamp,
                    updatedBy: createdBy,
                },
                {
                    tenant: tenantId,
                    name: "Eingangsrechnungen",
                    function: "yearSubCategory",
                    icon: "i-heroicons-inbox-arrow-down",
                    isSystemUsed: true,
                    updatedAt: timestamp,
                    updatedBy: createdBy,
                },
                {
                    tenant: tenantId,
                    name: "Belege Bankeinzahlung",
                    function: "yearSubCategory",
                    icon: "i-heroicons-banknotes",
                    isSystemUsed: true,
                    updatedAt: timestamp,
                    updatedBy: createdBy,
                },
            ])
            .returning({
                id: folders.id,
                name: folders.name,
            });

        const folderByName = new Map(insertedFolders.map((folder) => [folder.name, folder.id]));

        await server.db
            .insert(folders)
            .values([
                {
                    tenant: tenantId,
                    name: String(currentYear),
                    parent: folderByName.get("Ausgangsrechnungen"),
                    function: "invoices",
                    year: currentYear,
                    icon: "i-heroicons-document-text",
                    standardFiletype: invoiceTag?.id,
                    standardFiletypeIsOptional: false,
                    isSystemUsed: true,
                    updatedAt: timestamp,
                    updatedBy: createdBy,
                },
                {
                    tenant: tenantId,
                    name: String(currentYear),
                    parent: folderByName.get("Angebote"),
                    function: "quotes",
                    year: currentYear,
                    icon: "i-heroicons-document-duplicate",
                    standardFiletype: quoteTag?.id,
                    standardFiletypeIsOptional: false,
                    isSystemUsed: true,
                    updatedAt: timestamp,
                    updatedBy: createdBy,
                },
                {
                    tenant: tenantId,
                    name: String(currentYear),
                    parent: folderByName.get("Auftragsbestätigungen"),
                    function: "confirmationOrders",
                    year: currentYear,
                    icon: "i-heroicons-clipboard-document-check",
                    standardFiletype: confirmationTag?.id,
                    standardFiletypeIsOptional: false,
                    isSystemUsed: true,
                    updatedAt: timestamp,
                    updatedBy: createdBy,
                },
                {
                    tenant: tenantId,
                    name: String(currentYear),
                    parent: folderByName.get("Lieferscheine"),
                    function: "deliveryNotes",
                    year: currentYear,
                    icon: "i-heroicons-truck",
                    standardFiletype: deliveryTag?.id,
                    standardFiletypeIsOptional: false,
                    isSystemUsed: true,
                    updatedAt: timestamp,
                    updatedBy: createdBy,
                },
                {
                    tenant: tenantId,
                    name: String(currentYear),
                    parent: folderByName.get("Eingangsrechnungen"),
                    function: "incomingInvoices",
                    year: currentYear,
                    icon: "i-heroicons-inbox-arrow-down",
                    standardFiletype: incomingInvoiceTag?.id,
                    standardFiletypeIsOptional: false,
                    isSystemUsed: true,
                    updatedAt: timestamp,
                    updatedBy: createdBy,
                },
                {
                    tenant: tenantId,
                    name: String(currentYear),
                    parent: folderByName.get("Belege Bankeinzahlung"),
                    function: "deposit",
                    year: currentYear,
                    icon: "i-heroicons-banknotes",
                    isSystemUsed: true,
                    updatedAt: timestamp,
                    updatedBy: createdBy,
                },
            ]);
    };

    const tenantExportFilename = (tenantId: number, tenant?: { short?: string | null; name?: string | null }) => {
        const safeTenantName = String(tenant?.short || tenant?.name || tenantId)
            .replace(/[^a-z0-9_-]+/gi, "-")
            .replace(/^-+|-+$/g, "")
            .toLowerCase();

        return `fedeo-tenant-${safeTenantName || tenantId}-${new Date().toISOString().slice(0, 10)}.fedeo-export.zip`;
    };

    const lockTenantForJob = async (tenantId: number, jobId: string) => {
        const [tenant] = await server.db
            .select({
                locked: tenants.locked,
                lockedByExportJobId: tenants.lockedByExportJobId,
            })
            .from(tenants)
            .where(eq(tenants.id, tenantId))
            .limit(1);

        if (!tenant) throw new Error("Tenant not found");
        if (tenant.lockedByExportJobId && tenant.lockedByExportJobId !== jobId) {
            throw new Error("Tenant ist bereits durch einen anderen Export oder Import gesperrt");
        }

        await server.db
            .update(tenantExportJobs)
            .set({
                previousTenantLocked: tenant.locked,
                updatedAt: new Date(),
            })
            .where(eq(tenantExportJobs.id, jobId));

        await server.db
            .update(tenants)
            .set({
                locked: "maintenance_tenant",
                lockedByExportJobId: jobId,
                updatedAt: new Date(),
            })
            .where(eq(tenants.id, tenantId));
    };

    const unlockTenantForJob = async (tenantId: number, jobId: string) => {
        const [job] = await server.db
            .select({
                previousTenantLocked: tenantExportJobs.previousTenantLocked,
            })
            .from(tenantExportJobs)
            .where(eq(tenantExportJobs.id, jobId))
            .limit(1);

        await server.db
            .update(tenants)
            .set({
                locked: job?.previousTenantLocked || null,
                lockedByExportJobId: null,
                updatedAt: new Date(),
            })
            .where(and(
                eq(tenants.id, tenantId),
                eq(tenants.lockedByExportJobId, jobId)
            ));
    };

    const createTenantImportJob = async (tenantId: number, currentUserId: string, filename: string) => {
        const [job] = await server.db
            .insert(tenantExportJobs)
            .values({
                tenantId,
                createdBy: currentUserId,
                operation: "import",
                status: "running",
                filename,
                updatedAt: new Date(),
            })
            .returning();

        await lockTenantForJob(tenantId, job.id);

        return job;
    };

    const completeImportedTenantAccess = async (
        currentUser: { id: string; email: string },
        result: { tenantId: number }
    ) => {
        const fallbackName = deriveNameFromEmail(currentUser.email);

        await server.db
            .insert(authTenantUsers)
            .values({
                tenant_id: result.tenantId,
                user_id: currentUser.id,
                created_by: currentUser.id,
            })
            .onConflictDoNothing();

        const [existingAdminProfile] = await server.db
            .select({ id: authProfiles.id })
            .from(authProfiles)
            .where(and(
                eq(authProfiles.tenant_id, result.tenantId),
                eq(authProfiles.user_id, currentUser.id)
            ))
            .limit(1);

        if (!existingAdminProfile) {
            await server.db
                .insert(authProfiles)
                .values({
                    tenant_id: result.tenantId,
                    user_id: currentUser.id,
                    first_name: fallbackName.first_name,
                    last_name: fallbackName.last_name,
                    email: currentUser.email,
                    active: true,
                });
        }

        let matrixProvisioned = false;
        let matrixProvisioningError: string | null = null;
        if (process.env.MATRIX_REGISTRATION_SHARED_SECRET) {
            try {
                const matrix = matrixService(server);
                await matrix.provisionTenantRoom(currentUser.id, result.tenantId, {
                    key: "allgemein",
                    name: "Allgemeiner Chat",
                    type: "general",
                });
                matrixProvisioned = true;
            } catch (err: any) {
                matrixProvisioningError = err?.message || String(err);
                server.log.warn({ err }, "Matrix-Räume konnten nach Tenant-Import nicht neu provisioniert werden");
            }
        }

        return {
            matrixProvisioned,
            matrixProvisioningError,
        };
    };

    const startTenantImportJob = async (
        jobId: string,
        targetTenantId: number,
        currentUser: { id: string; email: string },
        importData: { type: "archive"; archiveBuffer: Buffer } | { type: "json"; exportData: TenantFullExport }
    ) => {
        try {
            await server.db
                .update(tenantExportJobs)
                .set({
                    status: "running",
                    filesDone: 0,
                    filesTotal: 1,
                    updatedAt: new Date(),
                })
                .where(eq(tenantExportJobs.id, jobId));

            const onProgress = async ({ done, total }: { done: number; total: number }) => {
                await server.db
                    .update(tenantExportJobs)
                    .set({
                        filesDone: done,
                        filesTotal: total,
                        updatedAt: new Date(),
                    })
                    .where(eq(tenantExportJobs.id, jobId));
            };

            const result = importData.type === "archive"
                ? await importTenantFullExportArchive(server, importData.archiveBuffer, { targetTenantId, onProgress })
                : await importTenantFullExport(server, importData.exportData, { targetTenantId, onProgress });

            await completeImportedTenantAccess(currentUser, result);

            await server.db
                .update(tenantExportJobs)
                .set({
                    status: "ready",
                    filesDone: 1,
                    filesTotal: 1,
                    completedAt: new Date(),
                    updatedAt: new Date(),
                    error: null,
                })
                .where(eq(tenantExportJobs.id, jobId));
        } catch (err: any) {
            console.error("ERROR tenant import job:", err);
            await server.db
                .update(tenantExportJobs)
                .set({
                    status: "failed",
                    error: err?.message || String(err),
                    completedAt: new Date(),
                    updatedAt: new Date(),
                })
                .where(eq(tenantExportJobs.id, jobId));
        } finally {
            await unlockTenantForJob(targetTenantId, jobId);
        }
    };

    const startTenantExportJob = async (jobId: string, tenantId: number, filename: string) => {
        try {
            await lockTenantForJob(tenantId, jobId);

            await server.db
                .update(tenantExportJobs)
                .set({
                    status: "running",
                    updatedAt: new Date(),
                })
                .where(eq(tenantExportJobs.id, jobId));

            const result = await createTenantFullExportArchive(server, tenantId, {
                filename,
                storagePath: `tenant-exports/${tenantId}/${jobId}/${filename}`,
                onProgress: async ({ filesDone, filesTotal }) => {
                    await server.db
                        .update(tenantExportJobs)
                        .set({
                            filesDone,
                            filesTotal,
                            updatedAt: new Date(),
                        })
                        .where(eq(tenantExportJobs.id, jobId));
                },
            });

            await server.db
                .update(tenantExportJobs)
                .set({
                    status: "ready",
                    storagePath: result.storagePath,
                    contentType: result.contentType,
                    fileSize: result.size,
                    filesTotal: result.filesTotal,
                    filesDone: result.filesDone,
                    completedAt: new Date(),
                    updatedAt: new Date(),
                    error: null,
                })
                .where(eq(tenantExportJobs.id, jobId));
        } catch (err: any) {
            console.error("ERROR tenant export job:", err);
            await server.db
                .update(tenantExportJobs)
                .set({
                    status: "failed",
                    error: err?.message || String(err),
                    completedAt: new Date(),
                    updatedAt: new Date(),
                })
                .where(eq(tenantExportJobs.id, jobId));
        } finally {
            await unlockTenantForJob(tenantId, jobId);
        }
    };

    const createPreparedTenantExport = async (tenantId: number, currentUserId: string) => {
        const [tenant] = await server.db
            .select({
                id: tenants.id,
                name: tenants.name,
                short: tenants.short,
            })
            .from(tenants)
            .where(eq(tenants.id, tenantId))
            .limit(1);

        if (!tenant) return null;

        const filename = tenantExportFilename(tenantId, tenant);
        const [job] = await server.db
            .insert(tenantExportJobs)
            .values({
                tenantId,
                createdBy: currentUserId,
                operation: "export",
                status: "queued",
                filename,
                updatedAt: new Date(),
            })
            .returning();

        void startTenantExportJob(job.id, tenantId, filename);

        return job;
    };

    const requireAdmin = async (req: FastifyRequest, reply: FastifyReply) => {
        if (!req.user?.user_id) {
            reply.code(401).send({ error: "Unauthorized" });
            return null;
        }

        const [currentUser] = await server.db
            .select({
                id: authUsers.id,
                email: authUsers.email,
                is_admin: authUsers.is_admin,
            })
            .from(authUsers)
            .where(eq(authUsers.id, req.user.user_id))
            .limit(1);

        if (!currentUser?.is_admin) {
            reply.code(403).send({ error: "Admin access required" });
            return null;
        }

        return currentUser;
    };

    const ensurePortalRoleForTenant = async (tenantId: number, createdBy: string) => {
        const existingRoles = await server.db
            .select({
                id: authRoles.id,
                name: authRoles.name,
            })
            .from(authRoles)
            .where(eq(authRoles.tenant_id, tenantId));

        const portalRole = existingRoles.find((role) => role.name === "Kundenportal");
        if (portalRole) return portalRole.id;

        const [createdRole] = await server.db
            .insert(authRoles)
            .values({
                name: "Kundenportal",
                description: "Automatisch angelegte Rolle für eingeladene Kundenportal-Benutzer",
                tenant_id: tenantId,
                created_by: createdBy,
            })
            .returning({
                id: authRoles.id,
            });

        return createdRole.id;
    };

    // -------------------------------------------------------------
    // GET /admin/overview
    // -------------------------------------------------------------
    server.get("/admin/overview", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const [tenantRows, userRows, profileRows, membershipRows, roleRows, roleAssignmentRows] = await Promise.all([
                server.db
                    .select({
                        id: tenants.id,
                        name: tenants.name,
                        short: tenants.short,
                        createdAt: tenants.createdAt,
                        locked: tenants.locked,
                        lockedByExportJobId: tenants.lockedByExportJobId,
                    })
                    .from(tenants),
                server.db
                    .select({
                        id: authUsers.id,
                        email: authUsers.email,
                        created_at: authUsers.created_at,
                        multiTenant: authUsers.multiTenant,
                        must_change_password: authUsers.must_change_password,
                        is_admin: authUsers.is_admin,
                    })
                    .from(authUsers),
                server.db
                    .select({
                        id: authProfiles.id,
                        user_id: authProfiles.user_id,
                        tenant_id: authProfiles.tenant_id,
                        first_name: authProfiles.first_name,
                        last_name: authProfiles.last_name,
                        full_name: authProfiles.full_name,
                        email: authProfiles.email,
                        active: authProfiles.active,
                    })
                    .from(authProfiles),
                server.db
                    .select()
                    .from(authTenantUsers),
                server.db
                    .select({
                        id: authRoles.id,
                        name: authRoles.name,
                        description: authRoles.description,
                        tenant_id: authRoles.tenant_id,
                    })
                    .from(authRoles),
                server.db
                    .select({
                        user_id: authUserRoles.user_id,
                        role_id: authUserRoles.role_id,
                        tenant_id: authUserRoles.tenant_id,
                    })
                    .from(authUserRoles),
            ]);
            const uniqueMembershipRows = Array.from(
                new Map(membershipRows.map((membership) => [
                    `${membership.tenant_id}:${membership.user_id}`,
                    membership,
                ])).values()
            );

            const users = userRows.map((user) => {
                const profiles = profileRows.filter((profile) => profile.user_id === user.id);
                const memberships = uniqueMembershipRows.filter((membership) => membership.user_id === user.id);
                const roleAssignments = roleAssignmentRows.filter((assignment) => assignment.user_id === user.id);
                const preferredProfile = profiles.find((profile) => profile.active) || profiles[0];
                const fallbackName = deriveNameFromEmail(user.email);

                return {
                    ...user,
                    display_name: preferredProfile?.full_name || user.email,
                    profile_defaults: {
                        first_name: preferredProfile?.first_name || fallbackName.first_name,
                        last_name: preferredProfile?.last_name || fallbackName.last_name,
                    },
                    profiles,
                    tenant_ids: memberships.map((membership) => membership.tenant_id),
                    role_assignments: roleAssignments,
                };
            });

            const tenantsWithCounts = tenantRows.map((tenant) => ({
                ...tenant,
                user_count: uniqueMembershipRows.filter((membership) => membership.tenant_id === tenant.id).length,
            }));

            return {
                users,
                tenants: tenantsWithCounts,
                roles: roleRows,
                unassignedProfiles: profileRows.filter((profile) => !profile.user_id),
                memberships: uniqueMembershipRows,
                roleAssignments: roleAssignmentRows,
            };
        } catch (err) {
            console.error("ERROR /admin/overview:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });

    // -------------------------------------------------------------
    // GET /admin/system-status
    // -------------------------------------------------------------
    server.get("/admin/system-status", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            return await buildSystemStatus(server);
        } catch (err) {
            console.error("ERROR /admin/system-status:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });

    // -------------------------------------------------------------
    // POST /admin/users
    // -------------------------------------------------------------
    server.post("/admin/users", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const body = req.body as {
                email?: string;
                password?: string;
                is_admin?: boolean;
                multiTenant?: boolean;
                first_name?: string;
                last_name?: string;
            };

            const email = body.email?.trim().toLowerCase();
            if (!email) {
                return reply.code(400).send({ error: "email required" });
            }

            const existingUsers = await server.db
                .select({ id: authUsers.id })
                .from(authUsers)
                .where(eq(authUsers.email, email))
                .limit(1);

            if (existingUsers.length) {
                return reply.code(409).send({ error: "User with this email already exists" });
            }

            const initialPassword = body.password?.trim() || generateRandomPassword(14);
            const passwordHash = await hashPassword(initialPassword);

            const [createdUser] = await server.db
                .insert(authUsers)
                .values({
                    email,
                    passwordHash,
                    is_admin: Boolean(body.is_admin),
                    multiTenant: typeof body.multiTenant === "boolean" ? body.multiTenant : true,
                    must_change_password: true,
                    updatedAt: new Date(),
                })
                .returning({
                    id: authUsers.id,
                    email: authUsers.email,
                    created_at: authUsers.created_at,
                    multiTenant: authUsers.multiTenant,
                    must_change_password: authUsers.must_change_password,
                    is_admin: authUsers.is_admin,
                });

            return {
                user: createdUser,
                initialPassword,
                profile_defaults: {
                    first_name: body.first_name?.trim() || deriveNameFromEmail(email).first_name,
                    last_name: body.last_name?.trim() || deriveNameFromEmail(email).last_name,
                },
            };
        } catch (err) {
            console.error("ERROR /admin/users:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });

    // -------------------------------------------------------------
    // POST /admin/profiles/:profileId/create-user
    // -------------------------------------------------------------
    server.post("/admin/profiles/:profileId/create-user", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const { profileId } = req.params as { profileId: string };
            const body = req.body as { email?: string };

            const email = body.email?.trim().toLowerCase();
            if (!email) {
                return reply.code(400).send({ error: "email required" });
            }

            const [profile] = await server.db
                .select({
                    id: authProfiles.id,
                    tenant_id: authProfiles.tenant_id,
                    user_id: authProfiles.user_id,
                    first_name: authProfiles.first_name,
                    last_name: authProfiles.last_name,
                    email: authProfiles.email,
                })
                .from(authProfiles)
                .where(eq(authProfiles.id, profileId))
                .limit(1);

            if (!profile) {
                return reply.code(404).send({ error: "Profile not found" });
            }

            if (profile.user_id) {
                return reply.code(409).send({ error: "Profile already linked to a user" });
            }

            const existingUsers = await server.db
                .select({ id: authUsers.id })
                .from(authUsers)
                .where(eq(authUsers.email, email))
                .limit(1);

            if (existingUsers.length) {
                return reply.code(409).send({ error: "User with this email already exists" });
            }

            const initialPassword = generateRandomPassword(14);
            const passwordHash = await hashPassword(initialPassword);

            const result = await server.db.transaction(async (tx) => {
                const [createdUser] = await tx
                    .insert(authUsers)
                    .values({
                        email,
                        passwordHash,
                        is_admin: false,
                        multiTenant: true,
                        must_change_password: true,
                        updatedAt: new Date(),
                    })
                    .returning({
                        id: authUsers.id,
                        email: authUsers.email,
                        must_change_password: authUsers.must_change_password,
                        is_admin: authUsers.is_admin,
                        multiTenant: authUsers.multiTenant,
                        created_at: authUsers.created_at,
                    });

                await tx
                    .insert(authTenantUsers)
                    .values({
                        tenant_id: profile.tenant_id,
                        user_id: createdUser.id,
                        created_by: currentUser.id,
                    });

                const [updatedProfile] = await tx
                    .update(authProfiles)
                    .set({
                        user_id: createdUser.id,
                        email,
                    })
                    .where(eq(authProfiles.id, profile.id))
                    .returning({
                        id: authProfiles.id,
                        tenant_id: authProfiles.tenant_id,
                        user_id: authProfiles.user_id,
                        first_name: authProfiles.first_name,
                        last_name: authProfiles.last_name,
                        email: authProfiles.email,
                    });

                return {
                    user: createdUser,
                    profile: updatedProfile,
                };
            });

            return {
                ...result,
                initialPassword,
            };
        } catch (err) {
            console.error("ERROR /admin/profiles/:profileId/create-user:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });

    server.post("/admin/customers/:customerId/invite-portal-user", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const tenantId = Number(req.user?.tenant_id);
            const { customerId } = req.params as { customerId: string };

            if (!tenantId) {
                return reply.code(400).send({ error: "No tenant selected" });
            }

            const [tenantRecord] = await server.db
                .select({
                    id: tenants.id,
                    name: tenants.name,
                    portalDomain: tenants.portalDomain,
                })
                .from(tenants)
                .where(eq(tenants.id, tenantId))
                .limit(1);

            const [customerRecord] = await server.db
                .select()
                .from(customers)
                .where(and(eq(customers.id, Number(customerId)), eq(customers.tenant, tenantId)))
                .limit(1);

            if (!customerRecord) {
                return reply.code(404).send({ error: "Customer not found" });
            }

            const customerInfo = customerRecord.infoData && typeof customerRecord.infoData === "object" ? customerRecord.infoData as Record<string, any> : {};
            const email = String(customerInfo.email || customerInfo.invoiceEmail || "").trim().toLowerCase();

            if (!email) {
                return reply.code(400).send({ error: "Customer has no email address" });
            }

            const generatedPassword = generateRandomPassword(14);
            const passwordHash = await hashPassword(generatedPassword);

            const [existingUser] = await server.db
                .select({
                    id: authUsers.id,
                    email: authUsers.email,
                    is_admin: authUsers.is_admin,
                })
                .from(authUsers)
                .where(eq(authUsers.email, email))
                .limit(1);

            const derivedName = deriveNameFromEmail(email);
            const firstName = customerRecord.firstname?.trim() || derivedName.first_name;
            const lastName = customerRecord.lastname?.trim() || derivedName.last_name;

            let userId = existingUser?.id || null;
            let createdNewUser = false;

            if (existingUser) {
                const [existingProfile] = await server.db
                    .select({
                        id: authProfiles.id,
                        customer_for_portal: authProfiles.customer_for_portal,
                    })
                    .from(authProfiles)
                    .where(and(
                        eq(authProfiles.user_id, existingUser.id),
                        eq(authProfiles.tenant_id, tenantId)
                    ))
                    .limit(1);

                if (existingUser.is_admin) {
                    return reply.code(409).send({ error: "Email address is already used by an admin user" });
                }

                if (!existingProfile) {
                    return reply.code(409).send({ error: "Email address is already used by another user" });
                }

                if (existingProfile.customer_for_portal && existingProfile.customer_for_portal !== customerRecord.id) {
                    return reply.code(409).send({ error: "Email address is already assigned to another portal customer" });
                }

                await server.db
                    .update(authUsers)
                    .set({
                        passwordHash,
                        must_change_password: true,
                        multiTenant: false,
                        updatedAt: new Date(),
                    })
                    .where(eq(authUsers.id, existingUser.id));

                userId = existingUser.id;
            } else {
                const [createdUser] = await server.db
                    .insert(authUsers)
                    .values({
                        email,
                        passwordHash,
                        is_admin: false,
                        multiTenant: false,
                        must_change_password: true,
                        updatedAt: new Date(),
                    })
                    .returning({
                        id: authUsers.id,
                    });

                userId = createdUser.id;
                createdNewUser = true;
            }

            const portalRoleId = await ensurePortalRoleForTenant(tenantId, currentUser.id);

            const existingMemberships = await server.db
                .select()
                .from(authTenantUsers)
                .where(and(
                    eq(authTenantUsers.user_id, userId!),
                    eq(authTenantUsers.tenant_id, tenantId)
                ))
                .limit(1);

            if (!existingMemberships.length) {
                await server.db
                    .insert(authTenantUsers)
                    .values({
                        tenant_id: tenantId,
                        user_id: userId!,
                        created_by: currentUser.id,
                    });
            }

            const existingPortalRoleAssignment = await server.db
                .select()
                .from(authUserRoles)
                .where(and(
                    eq(authUserRoles.user_id, userId!),
                    eq(authUserRoles.tenant_id, tenantId),
                    eq(authUserRoles.role_id, portalRoleId)
                ))
                .limit(1);

            if (!existingPortalRoleAssignment.length) {
                await server.db
                    .insert(authUserRoles)
                    .values({
                        user_id: userId!,
                        tenant_id: tenantId,
                        role_id: portalRoleId,
                        created_by: currentUser.id,
                    });
            }

            const [existingTenantProfile] = await server.db
                .select({
                    id: authProfiles.id,
                    user_id: authProfiles.user_id,
                    customer_for_portal: authProfiles.customer_for_portal,
                })
                .from(authProfiles)
                .where(and(
                    eq(authProfiles.user_id, userId!),
                    eq(authProfiles.tenant_id, tenantId)
                ))
                .limit(1);

            if (existingTenantProfile) {
                await server.db
                    .update(authProfiles)
                    .set({
                        first_name: firstName,
                        last_name: lastName,
                        email,
                        customer_for_portal: customerRecord.id,
                        active: true,
                    })
                    .where(eq(authProfiles.id, existingTenantProfile.id));
            } else {
                await server.db
                    .insert(authProfiles)
                    .values({
                        user_id: userId!,
                        tenant_id: tenantId,
                        first_name: firstName,
                        last_name: lastName,
                        email,
                        customer_for_portal: customerRecord.id,
                        active: true,
                    });
            }

            const portalUrl = tenantRecord?.portalDomain ? `https://${tenantRecord.portalDomain}/login` : null;

            const mailResult = await sendMail(
                email,
                `FEDEO | Einladung ins Kundenportal`,
                `
                    <p>Hallo${customerRecord.name ? ` ${customerRecord.name}` : ""},</p>
                    <p>für Sie wurde ein Zugang zum FEDEO Kundenportal eingerichtet.</p>
                    <p><strong>E-Mail:</strong> ${email}</p>
                    <p><strong>Initialpasswort:</strong> ${generatedPassword}</p>
                    <p>Bitte ändern Sie dieses Passwort direkt nach dem ersten Login.</p>
                    ${portalUrl ? `<p><strong>Login:</strong> <a href="${portalUrl}">${portalUrl}</a></p>` : ""}
                    <p>Viele Grüße<br>${tenantRecord?.name || "FEDEO"}</p>
                `
            );

            if (!mailResult.success) {
                return reply.code(500).send({ error: "Invitation email could not be sent" });
            }

            return {
                success: true,
                createdNewUser,
                email,
                initialPassword: generatedPassword,
                portalUrl,
            };
        } catch (err) {
            console.error("ERROR /admin/customers/:customerId/invite-portal-user:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });

    // -------------------------------------------------------------
    // POST /admin/tenants
    // -------------------------------------------------------------
    server.post("/admin/tenants", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const body = req.body as {
                name?: string;
                short?: string;
                locked?: string | null;
            };

            const name = body.name?.trim();
            const short = body.short?.trim();

            if (!name || !short) {
                return reply.code(400).send({ error: "name and short required" });
            }

            const [createdTenant] = await server.db
                .insert(tenants)
                .values({
                    name,
                    short,
                    updatedAt: new Date(),
                    updatedBy: currentUser.id,
                })
                .returning({
                    id: tenants.id,
                    name: tenants.name,
                    short: tenants.short,
                    createdAt: tenants.createdAt,
                    locked: tenants.locked,
                    lockedByExportJobId: tenants.lockedByExportJobId,
                });

            await createTenantSeeds(createdTenant.id, currentUser.id);
            await ensureTenantBaseData(server, createdTenant.id, currentUser.id);

            return { tenant: createdTenant };
        } catch (err) {
            console.error("ERROR /admin/tenants:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });

    // -------------------------------------------------------------
    // PUT /admin/users/:user_id
    // -------------------------------------------------------------
    server.put("/admin/users/:user_id", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const { user_id } = req.params as { user_id: string };
            const body = req.body as {
                email?: string;
                multiTenant?: boolean;
                must_change_password?: boolean;
                is_admin?: boolean;
            };

            const updateData: Record<string, any> = {
                updatedAt: new Date(),
            };

            if (typeof body.email === "string") updateData.email = body.email.trim().toLowerCase();
            if (typeof body.multiTenant === "boolean") updateData.multiTenant = body.multiTenant;
            if (typeof body.must_change_password === "boolean") updateData.must_change_password = body.must_change_password;
            if (typeof body.is_admin === "boolean") updateData.is_admin = body.is_admin;

            const [updatedUser] = await server.db
                .update(authUsers)
                .set(updateData)
                .where(eq(authUsers.id, user_id))
                .returning({
                    id: authUsers.id,
                    email: authUsers.email,
                    created_at: authUsers.created_at,
                    multiTenant: authUsers.multiTenant,
                    must_change_password: authUsers.must_change_password,
                    is_admin: authUsers.is_admin,
                });

            if (!updatedUser) {
                return reply.code(404).send({ error: "User not found" });
            }

            return { user: updatedUser };
        } catch (err) {
            console.error("ERROR /admin/users/:user_id:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });

    // -------------------------------------------------------------
    // PUT /admin/tenants/:tenant_id
    // -------------------------------------------------------------
    server.put("/admin/tenants/:tenant_id", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const { tenant_id } = req.params as { tenant_id: string };
            const body = req.body as {
                name?: string;
                short?: string;
                locked?: string | null;
            };

            const updateData: Record<string, any> = {
                updatedAt: new Date(),
                updatedBy: currentUser.id,
            };

            if (typeof body.name === "string") updateData.name = body.name.trim();
            if (typeof body.short === "string") updateData.short = body.short.trim();
            if (Object.prototype.hasOwnProperty.call(body, "locked")) {
                const allowedLockedValues = new Set([null, "maintenance_tenant", "maintenance", "general", "no_subscription"]);
                const lockedValue = body.locked || null;
                if (!allowedLockedValues.has(lockedValue)) {
                    return reply.code(400).send({ error: "Invalid locked value" });
                }

                updateData.locked = lockedValue;
                updateData.lockedByExportJobId = null;
            }

            const [updatedTenant] = await server.db
                .update(tenants)
                .set(updateData)
                .where(eq(tenants.id, Number(tenant_id)))
                .returning({
                    id: tenants.id,
                    name: tenants.name,
                    short: tenants.short,
                    createdAt: tenants.createdAt,
                    locked: tenants.locked,
                    lockedByExportJobId: tenants.lockedByExportJobId,
                });

            if (!updatedTenant) {
                return reply.code(404).send({ error: "Tenant not found" });
            }

            return { tenant: updatedTenant };
        } catch (err) {
            console.error("ERROR /admin/tenants/:tenant_id:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });

    // -------------------------------------------------------------
    // GET /admin/tenants/:tenant_id/export
    // -------------------------------------------------------------
    server.get("/admin/tenants/:tenant_id/export", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const { tenant_id } = req.params as { tenant_id: string };
            const tenantId = Number(tenant_id);
            if (!tenantId) {
                return reply.code(400).send({ error: "tenant_id required" });
            }

            const job = await createPreparedTenantExport(tenantId, currentUser.id);
            if (!job) {
                return reply.code(404).send({ error: "Tenant not found" });
            }

            return reply.code(202).send({
                exportId: job.id,
                status: job.status,
                filename: job.filename,
                statusUrl: `/api/admin/tenant-exports/${job.id}`,
                downloadUrl: `/api/admin/tenant-exports/${job.id}/download`,
            });
        } catch (err) {
            console.error("ERROR /admin/tenants/:tenant_id/export:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });

    // -------------------------------------------------------------
    // POST /admin/tenants/:tenant_id/exports
    // -------------------------------------------------------------
    server.post("/admin/tenants/:tenant_id/exports", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const { tenant_id } = req.params as { tenant_id: string };
            const tenantId = Number(tenant_id);
            if (!tenantId) {
                return reply.code(400).send({ error: "tenant_id required" });
            }

            const job = await createPreparedTenantExport(tenantId, currentUser.id);
            if (!job) {
                return reply.code(404).send({ error: "Tenant not found" });
            }

            return reply.code(202).send({
                exportId: job.id,
                status: job.status,
                filename: job.filename,
                statusUrl: `/api/admin/tenant-exports/${job.id}`,
                downloadUrl: `/api/admin/tenant-exports/${job.id}/download`,
            });
        } catch (err) {
            console.error("ERROR /admin/tenants/:tenant_id/exports:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });

    // -------------------------------------------------------------
    // GET /admin/tenant-exports/:export_id
    // -------------------------------------------------------------
    server.get("/admin/tenant-exports/:export_id", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const { export_id } = req.params as { export_id: string };
            const [job] = await server.db
                .select()
                .from(tenantExportJobs)
                .where(eq(tenantExportJobs.id, export_id))
                .limit(1);

            if (!job) return reply.code(404).send({ error: "Export not found" });

            return {
                exportId: job.id,
                tenantId: job.tenantId,
                operation: job.operation,
                status: job.status,
                filename: job.filename,
                fileSize: job.fileSize,
                filesDone: job.filesDone,
                filesTotal: job.filesTotal,
                error: job.error,
                createdAt: job.createdAt,
                updatedAt: job.updatedAt,
                completedAt: job.completedAt,
                downloadUrl: job.operation === "export" && job.status === "ready" ? `/api/admin/tenant-exports/${job.id}/download` : null,
            };
        } catch (err) {
            console.error("ERROR /admin/tenant-exports/:export_id:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });

    // -------------------------------------------------------------
    // GET /admin/tenant-exports/:export_id/download
    // -------------------------------------------------------------
    server.get("/admin/tenant-exports/:export_id/download", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const { export_id } = req.params as { export_id: string };
            const [job] = await server.db
                .select()
                .from(tenantExportJobs)
                .where(eq(tenantExportJobs.id, export_id))
                .limit(1);

            if (!job) return reply.code(404).send({ error: "Export not found" });
            if (job.status !== "ready" || !job.storagePath) {
                return reply.code(409).send({ error: "Export not ready", status: job.status });
            }

            const { Body, ContentLength } = await s3.send(new GetObjectCommand({
                Bucket: secrets.S3_BUCKET,
                Key: job.storagePath,
            }));

            reply.header("Content-Type", job.contentType || "application/zip");
            reply.header("Content-Disposition", `attachment; filename="${job.filename}"`);
            if (ContentLength) reply.header("Content-Length", String(ContentLength));
            return reply.send(Body as any);
        } catch (err) {
            console.error("ERROR /admin/tenant-exports/:export_id/download:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });

    // -------------------------------------------------------------
    // POST /admin/tenant-imports
    // -------------------------------------------------------------
    server.post("/admin/tenant-imports", { bodyLimit: 1024 * 1024 * 1024 }, async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const isMultipart = req.headers["content-type"]?.includes("multipart/form-data");
            let result;
            let targetTenantId: number | null = null;

            if (isMultipart) {
                const data: any = await req.file();
                if (!data?.file) return reply.code(400).send({ error: "export file required" });

                const archiveBuffer = await data.toBuffer();
                targetTenantId = Number(data.fields?.targetTenantId?.value || 0) || null;

                if (targetTenantId) {
                    const importJob = await createTenantImportJob(targetTenantId, currentUser.id, data.filename || "tenant-import.zip");
                    void startTenantImportJob(importJob.id, targetTenantId, currentUser, {
                        type: "archive",
                        archiveBuffer,
                    });

                    return reply.code(202).send({
                        importId: importJob.id,
                        exportId: importJob.id,
                        tenantId: targetTenantId,
                        status: importJob.status,
                        filename: importJob.filename,
                        statusUrl: `/api/admin/tenant-exports/${importJob.id}`,
                    });
                }

                result = await importTenantFullExportArchive(server, archiveBuffer, { targetTenantId });
            } else {
                const body = req.body as TenantFullExport | { exportData?: TenantFullExport; targetTenantId?: number };
                const exportData = "format" in body ? body : body.exportData;
                targetTenantId = "format" in body ? null : Number(body.targetTenantId || 0) || null;

                if (!exportData) {
                    return reply.code(400).send({ error: "exportData required" });
                }

                if (targetTenantId) {
                    const importJob = await createTenantImportJob(targetTenantId, currentUser.id, "tenant-import.json");
                    void startTenantImportJob(importJob.id, targetTenantId, currentUser, {
                        type: "json",
                        exportData,
                    });

                    return reply.code(202).send({
                        importId: importJob.id,
                        exportId: importJob.id,
                        tenantId: targetTenantId,
                        status: importJob.status,
                        filename: importJob.filename,
                        statusUrl: `/api/admin/tenant-exports/${importJob.id}`,
                    });
                }

                result = await importTenantFullExport(server, exportData, { targetTenantId });
            }

            const { matrixProvisioned, matrixProvisioningError } = await completeImportedTenantAccess(currentUser, result);

            return {
                success: true,
                matrixProvisioned,
                matrixProvisioningError,
                ...result,
            };
        } catch (err: any) {
            console.error("ERROR /admin/tenant-imports:", err);
            return reply.code(500).send({ error: err?.message || "Internal Server Error" });
        }
    });

    // -------------------------------------------------------------
    // PUT /admin/users/:user_id/access
    // -------------------------------------------------------------
    server.put("/admin/users/:user_id/access", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const { user_id } = req.params as { user_id: string };
            const body = req.body as {
                tenant_ids?: number[];
                role_assignments?: { tenant_id: number; role_id: string }[];
                profile_defaults?: { first_name?: string; last_name?: string };
                profile_assignments?: { tenant_id: number; profile_id?: string | null }[];
            };

            const tenantIds = Array.from(new Set((body.tenant_ids || []).map((tenantId) => Number(tenantId)).filter(Boolean)));
            const requestedAssignments = (body.role_assignments || [])
                .map((assignment) => ({
                    tenant_id: Number(assignment.tenant_id),
                    role_id: assignment.role_id,
                }))
                .filter((assignment) => assignment.tenant_id && assignment.role_id && tenantIds.includes(assignment.tenant_id));

            const [targetUser] = await server.db
                .select({ id: authUsers.id, email: authUsers.email })
                .from(authUsers)
                .where(eq(authUsers.id, user_id))
                .limit(1);

            if (!targetUser) {
                return reply.code(404).send({ error: "User not found" });
            }

            const availableRoles = requestedAssignments.length
                ? await server.db
                    .select({
                        id: authRoles.id,
                        tenant_id: authRoles.tenant_id,
                    })
                    .from(authRoles)
                    .where(
                        inArray(
                            authRoles.id,
                            requestedAssignments.map((assignment) => assignment.role_id)
                        )
                    )
                : [];

            const validRoleIds = new Set(
                availableRoles
                    .filter((role) =>
                        role.tenant_id === null ||
                        requestedAssignments.some((assignment) => assignment.role_id === role.id && assignment.tenant_id === role.tenant_id)
                    )
                    .map((role) => role.id)
            );

            const validAssignments = requestedAssignments.filter((assignment) => validRoleIds.has(assignment.role_id));
            const existingMemberships = await server.db
                .select()
                .from(authTenantUsers)
                .where(eq(authTenantUsers.user_id, user_id));
            const removedTenantIds = existingMemberships
                .map((membership) => membership.tenant_id)
                .filter((tenantId) => !tenantIds.includes(tenantId));

            const existingUserProfiles = await server.db
                .select({
                    id: authProfiles.id,
                    tenant_id: authProfiles.tenant_id,
                })
                .from(authProfiles)
                .where(eq(authProfiles.user_id, user_id));

            const unassignedProfiles = tenantIds.length
                ? await server.db
                    .select({
                        id: authProfiles.id,
                        tenant_id: authProfiles.tenant_id,
                    })
                    .from(authProfiles)
                    .where(
                        and(
                            inArray(authProfiles.tenant_id, tenantIds),
                            isNull(authProfiles.user_id)
                        )
                    )
                : [];

            const fallbackName = deriveNameFromEmail(targetUser.email);
            const profileDefaults = {
                first_name: body.profile_defaults?.first_name?.trim() || fallbackName.first_name,
                last_name: body.profile_defaults?.last_name?.trim() || fallbackName.last_name,
            };
            const requestedProfileAssignments = new Map<number, string>(
                (body.profile_assignments || [])
                    .filter((assignment) => assignment?.tenant_id && assignment.profile_id)
                    .map((assignment) => [Number(assignment.tenant_id), String(assignment.profile_id)])
            );

            await server.db
                .delete(authUserRoles)
                .where(eq(authUserRoles.user_id, user_id));

            await server.db
                .delete(authTenantUsers)
                .where(eq(authTenantUsers.user_id, user_id));

            if (tenantIds.length) {
                await server.db
                    .insert(authTenantUsers)
                    .values(
                        tenantIds.map((tenantId) => ({
                            tenant_id: tenantId,
                            user_id,
                            created_by: currentUser.id,
                        }))
                    );
            }

            if (validAssignments.length) {
                await server.db
                    .insert(authUserRoles)
                    .values(
                        validAssignments.map((assignment) => ({
                            user_id,
                            tenant_id: assignment.tenant_id,
                            role_id: assignment.role_id,
                            created_by: currentUser.id,
                        }))
                    );
            }

            if (removedTenantIds.length) {
                await server.db
                    .update(authProfiles)
                    .set({ user_id: null })
                    .where(
                        and(
                            eq(authProfiles.user_id, user_id),
                            inArray(authProfiles.tenant_id, removedTenantIds)
                        )
                    );
            }

            const existingProfileTenantIds = new Set(existingUserProfiles.map((profile) => profile.tenant_id));

            for (const tenantId of tenantIds) {
                if (existingProfileTenantIds.has(tenantId)) continue;

                const requestedProfileId = requestedProfileAssignments.get(tenantId);
                const freeProfile = requestedProfileId
                    ? unassignedProfiles.find((profile) => profile.id === requestedProfileId && profile.tenant_id === tenantId)
                    : null;

                if (freeProfile) {
                    await server.db
                        .update(authProfiles)
                        .set({ user_id })
                        .where(eq(authProfiles.id, freeProfile.id));
                    continue;
                }

                await server.db
                    .insert(authProfiles)
                    .values({
                        user_id,
                        tenant_id: tenantId,
                        first_name: profileDefaults.first_name,
                        last_name: profileDefaults.last_name,
                        email: targetUser.email,
                        active: true,
                    });
            }

            return {
                success: true,
                tenant_ids: tenantIds,
                role_assignments: validAssignments,
            };
        } catch (err) {
            console.error("ERROR /admin/users/:user_id/access:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });

    // -------------------------------------------------------------
    // POST /admin/add-user-to-tenant
    // -------------------------------------------------------------
    server.post("/admin/add-user-to-tenant", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const body = req.body as {
                user_id: string;
                tenant_id: number;
                role?: string;
                mode?: "single" | "multi";
            };

            if (!body.user_id || !body.tenant_id) {
                return reply.code(400).send({
                    error: "user_id and tenant_id required"
                });
            }

            const mode = body.mode ?? "multi";

            // ----------------------------
            // SINGLE MODE → alte Verknüpfungen löschen
            // ----------------------------
            if (mode === "single") {
                await server.db
                    .delete(authTenantUsers)
                    .where(eq(authTenantUsers.user_id, body.user_id));
            }

            // ----------------------------
            // Neue Verknüpfung hinzufügen
            // ----------------------------

            await server.db
                .insert(authTenantUsers)
                .values({
                    user_id: body.user_id,
                    tenant_id: body.tenant_id,
                    created_by: currentUser.id,
                });

            return { success: true, mode };

        } catch (err) {
            console.error("ERROR /admin/add-user-to-tenant:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });


    // -------------------------------------------------------------
    // GET /admin/user-tenants/:user_id
    // -------------------------------------------------------------
    server.get("/admin/user-tenants/:user_id", async (req, reply) => {
        try {
            const currentUser = await requireAdmin(req, reply);
            if (!currentUser) return;

            const { user_id } = req.params as { user_id: string };

            if (!user_id) {
                return reply.code(400).send({ error: "user_id required" });
            }

            // ----------------------------
            // 1) User existiert?
            // ----------------------------
            const [user] = await server.db
                .select()
                .from(authUsers)
                .where(eq(authUsers.id, user_id))
                .limit(1);

            if (!user) {
                return reply.code(400).send({ error: "faulty user_id presented" });
            }

            // ----------------------------
            // 2) Tenants Join über auth_tenant_users
            // ----------------------------
            const tenantRecords = await server.db
                .select({
                    id: tenants.id,
                    name: tenants.name,
                    short: tenants.short,
                    locked: tenants.locked,
                    numberRanges: tenants.numberRanges,
                    accountChart: tenants.accountChart,
                    extraModules: tenants.extraModules,
                })
                .from(authTenantUsers)
                .innerJoin(
                    tenants,
                    eq(authTenantUsers.tenant_id, tenants.id)
                )
                .where(eq(authTenantUsers.user_id, user_id));

            return {
                user_id,
                tenants: tenantRecords,
            };

        } catch (err) {
            console.error("ERROR /admin/user-tenants:", err);
            return reply.code(500).send({ error: "Internal Server Error" });
        }
    });

}
