import { createHmac, randomBytes } from "node:crypto";
import type { DataStore } from "../db/store";
import type { Role, Token } from "../db/types";

type TokenContext = {
    tokenId: string;
    streamId: string;
    role: Role;
};

type IssuedToken = {
    plainToken: string;
    token: Token;
};

function hashToken(plainToken: string, pepper: string): string {
    return createHmac("sha256", pepper).update(plainToken, "utf8").digest("hex");
}

function generatePlainToken(): string {
    return `cdt_${randomBytes(24).toString("base64url")}`;
}

export async function issueToken(
    store: DataStore,
    streamId: string,
    role: Role,
    pepper: string
): Promise<IssuedToken> {
    const stream = await store.getStreamById(streamId);
    if (!stream) {
        throw new Error(`Cannot issue token: stream not found (${streamId})`);
    }

    const plainToken = generatePlainToken();
    const tokenHash = hashToken(plainToken, pepper);
    const token = await store.createToken({ streamId, role, tokenHash });

    return { plainToken, token };
}

export async function resolveToken(
    store: DataStore,
    plainToken: string | null | undefined,
    pepper: string
): Promise<TokenContext | null> {
    if (!plainToken || !plainToken.trim()) {
        return null;
    }

    const tokenHash = hashToken(plainToken.trim(), pepper);
    const token = await store.getTokenByHash(tokenHash);
    if (!token) {
        return null;
    }

    return {
        tokenId: token.id,
        streamId: token.streamId,
        role: token.role
    };
}

export async function revokeToken(store: DataStore, tokenId: string): Promise<boolean> {
    return store.revokeTokenById(tokenId);
}

export async function listStreamTokens(
    store: DataStore,
    streamId: string
): Promise<Token[]> {
    return store.listTokensByStream(streamId);
}
