import { FastifyInstance, FastifyRequest } from "fastify"
import { and, eq, or, isNull, inArray } from "drizzle-orm"
import {
    authRoles,
    authRolePermissions,
    authUserRoles,
} from "../../db/schema"
import { McpContext, McpTool } from "./types"

export async function loadTenantPermissions(
    server: FastifyInstance,
    userId: string,
    tenantId: number
) {
    const roleRows = await server.db
        .select({
            roleId: authUserRoles.role_id,
        })
        .from(authUserRoles)
        .innerJoin(
            authRoles,
            and(
                eq(authRoles.id, authUserRoles.role_id),
                or(isNull(authRoles.tenant_id), eq(authRoles.tenant_id, tenantId))
            )
        )
        .where(
            and(
                eq(authUserRoles.user_id, userId),
                eq(authUserRoles.tenant_id, tenantId)
            )
        )

    const roleIds = Array.from(new Set(roleRows.map((row) => row.roleId)))

    if (roleIds.length === 0) return []

    const permissionRows = await server.db
        .select({
            permission: authRolePermissions.permission,
        })
        .from(authRolePermissions)
        .where(inArray(authRolePermissions.role_id, roleIds))

    return Array.from(new Set(permissionRows.map((row) => row.permission)))
}

export async function createMcpContext(
    server: FastifyInstance,
    request: FastifyRequest
): Promise<McpContext> {
    const user = request.user

    if (!user?.user_id) {
        throw Object.assign(new Error("Authentication required"), { statusCode: 401 })
    }

    if (!user.tenant_id) {
        throw Object.assign(new Error("MCP benötigt einen aktiven Mandanten"), { statusCode: 403 })
    }

    const permissions = await loadTenantPermissions(server, user.user_id, user.tenant_id)

    return {
        server,
        request,
        tenantId: user.tenant_id,
        userId: user.user_id,
        isAdmin: Boolean(user.is_admin),
        permissions,
    }
}

export function assertToolPermission(context: McpContext, tool: McpTool) {
    if (context.isAdmin) return

    const allowed = tool.requiredPermissions.every((permission) =>
        context.permissions.includes(permission)
    )

    if (!allowed) {
        throw Object.assign(
            new Error(`Fehlende Berechtigung für ${tool.name}: ${tool.requiredPermissions.join(", ")}`),
            { statusCode: 403 }
        )
    }
}

