import crypto from 'node:crypto';
import { promisify } from 'node:util';
import { Types } from 'mongoose';
import { z } from 'zod';
import { ApiTokenModel } from './mongoose-models/ApiTokenModel.js';
import { getUserId } from './organization.js';
// Convert callback-based pbkdf2 to a Promise-based function
const pbkdf2 = promisify(crypto.pbkdf2);
const hashToken = async (token, salt) => {
    const derivedKey = await pbkdf2(token, salt, 10000, 64, 'sha512');
    return derivedKey.toString('hex');
};
export const tokenCreatorInputParser = z.object({
    createdBy: z.string(),
    organizationId: z.string(),
    tokenName: z.string(),
    tokenDescription: z.string(),
});
export const tokenEditInputParser = z.object({
    tokenId: z.string(),
    organizationId: z.string(),
    tokenName: z.string(),
    tokenDescription: z.string(),
});
export const tokenVerifierInputParser = z.object({
    organizationId: z.string(),
    token: z.string(),
});
export const tokenRetrieverInputParser = z.object({
    tokenId: z.string(),
});
export const tokenDeleteInputParser = z.object({
    organizationId: z.string(),
    ...tokenRetrieverInputParser.shape,
});
export const createToken = async ({ createdBy, organizationId, tokenName, tokenDescription, }) => {
    const token = crypto.randomBytes(16).toString('hex');
    const salt = crypto.randomBytes(16).toString('hex');
    const hashedToken = await hashToken(token, salt);
    const user = await getUserId(createdBy);
    if (!user)
        return null;
    const saveToken = await new ApiTokenModel({
        hashedToken,
        salt,
        createdBy: user._id,
        organizationId: new Types.ObjectId(organizationId),
        tokenName,
        tokenDescription,
    }).save();
    if (!saveToken)
        return null;
    return { token, tokenName, tokenId: saveToken._id };
};
export const getToken = async ({ tokenId, }) => {
    return ApiTokenModel.findOne({
        _id: new Types.ObjectId(tokenId),
    }).lean();
};
export const updateToken = async ({ tokenId, organizationId, tokenName, tokenDescription, }) => {
    return ApiTokenModel.updateOne({
        _id: new Types.ObjectId(tokenId),
        organizationId: new Types.ObjectId(organizationId),
    }, {
        tokenName,
        tokenDescription,
    });
};
export const verifyToken = async ({ organizationId, token, }) => {
    const apiToken = await ApiTokenModel.findOne({
        organizationId: new Types.ObjectId(organizationId),
    });
    if (!apiToken) {
        return false;
    }
    const computedHash = await hashToken(token, apiToken.salt);
    return computedHash === apiToken.hashedToken;
};
export const deleteToken = async ({ organizationId, tokenId, }) => {
    return ApiTokenModel.deleteOne({
        _id: new Types.ObjectId(tokenId),
        organizationId: new Types.ObjectId(organizationId),
    });
};
export const organizationInputParser = z.object({
    organizationId: z.string(),
});
export const getOrganizationApiTokens = async ({ organizationId, }) => {
    return ApiTokenModel.find({
        organizationId: new Types.ObjectId(organizationId),
    }).lean();
};
