import { createHash } from "node:crypto";
import { and, eq, isNull } from "drizzle-orm";
import { agentApiKeys, agents, companyMemberships, instanceUserRoles } from "@zmeel/db";
import { verifyLocalAgentJwt } from "../agent-auth-jwt.js";
import { logger } from "./logger.js";
import { boardAuthService } from "../services/board-auth.js";
function hashToken(token) {
    return createHash("sha256").update(token).digest("hex");
}
export function actorMiddleware(db, opts) {
    const boardAuth = boardAuthService(db);
    return async (req, _res, next) => {
        req.actor =
            opts.deploymentMode === "local_trusted"
                ? { type: "board", userId: "local-board", isInstanceAdmin: true, source: "local_implicit" }
                : { type: "none", source: "none" };
        const runIdHeader = req.header("x-zmeel-run-id");
        const authHeader = req.header("authorization");
        if (!authHeader?.toLowerCase().startsWith("bearer ")) {
            if (opts.deploymentMode === "authenticated" && opts.resolveSession) {
                let session = null;
                try {
                    session = await opts.resolveSession(req);
                }
                catch (err) {
                    logger.warn({ err, method: req.method, url: req.originalUrl }, "Failed to resolve auth session from request headers");
                }
                if (session?.user?.id) {
                    const userId = session.user.id;
                    const [roleRow, memberships] = await Promise.all([
                        db
                            .select({ id: instanceUserRoles.id })
                            .from(instanceUserRoles)
                            .where(and(eq(instanceUserRoles.userId, userId), eq(instanceUserRoles.role, "instance_admin")))
                            .then((rows) => rows[0] ?? null),
                        db
                            .select({ companyId: companyMemberships.companyId })
                            .from(companyMemberships)
                            .where(and(eq(companyMemberships.principalType, "user"), eq(companyMemberships.principalId, userId), eq(companyMemberships.status, "active"))),
                    ]);
                    req.actor = {
                        type: "board",
                        userId,
                        companyIds: memberships.map((row) => row.companyId),
                        isInstanceAdmin: Boolean(roleRow),
                        runId: runIdHeader ?? undefined,
                        source: "session",
                    };
                    next();
                    return;
                }
            }
            if (runIdHeader)
                req.actor.runId = runIdHeader;
            next();
            return;
        }
        const token = authHeader.slice("bearer ".length).trim();
        if (!token) {
            next();
            return;
        }
        const boardKey = await boardAuth.findBoardApiKeyByToken(token);
        if (boardKey) {
            const access = await boardAuth.resolveBoardAccess(boardKey.userId);
            if (access.user) {
                await boardAuth.touchBoardApiKey(boardKey.id);
                req.actor = {
                    type: "board",
                    userId: boardKey.userId,
                    companyIds: access.companyIds,
                    isInstanceAdmin: access.isInstanceAdmin,
                    keyId: boardKey.id,
                    runId: runIdHeader || undefined,
                    source: "board_key",
                };
                next();
                return;
            }
        }
        const tokenHash = hashToken(token);
        const key = await db
            .select()
            .from(agentApiKeys)
            .where(and(eq(agentApiKeys.keyHash, tokenHash), isNull(agentApiKeys.revokedAt)))
            .then((rows) => rows[0] ?? null);
        if (!key) {
            const claims = verifyLocalAgentJwt(token);
            if (!claims) {
                next();
                return;
            }
            const agentRecord = await db
                .select()
                .from(agents)
                .where(eq(agents.id, claims.sub))
                .then((rows) => rows[0] ?? null);
            if (!agentRecord || agentRecord.companyId !== claims.company_id) {
                next();
                return;
            }
            if (agentRecord.status === "terminated" || agentRecord.status === "pending_approval") {
                next();
                return;
            }
            req.actor = {
                type: "agent",
                agentId: claims.sub,
                companyId: claims.company_id,
                keyId: undefined,
                runId: runIdHeader || claims.run_id || undefined,
                source: "agent_jwt",
            };
            next();
            return;
        }
        await db
            .update(agentApiKeys)
            .set({ lastUsedAt: new Date() })
            .where(eq(agentApiKeys.id, key.id));
        const agentRecord = await db
            .select()
            .from(agents)
            .where(eq(agents.id, key.agentId))
            .then((rows) => rows[0] ?? null);
        if (!agentRecord || agentRecord.status === "terminated" || agentRecord.status === "pending_approval") {
            next();
            return;
        }
        req.actor = {
            type: "agent",
            agentId: key.agentId,
            companyId: key.companyId,
            keyId: key.id,
            runId: runIdHeader || undefined,
            source: "agent_key",
        };
        next();
    };
}
export function requireBoard(req) {
    return req.actor.type === "board";
}
//# sourceMappingURL=auth.js.map