import { rmSync } from "node:fs";
import { resolve } from "node:path";
import { runCli } from "./commands";

function assert(condition: boolean, message: string): void {
    if (!condition) {
        throw new Error(message);
    }
}

function parseSingleJson(lines: string[]): Record<string, unknown> {
    const merged = lines.join("\n").trim();
    return JSON.parse(merged) as Record<string, unknown>;
}

async function main(): Promise<void> {
    const smokeDataFile = resolve(
        process.cwd(),
        "data",
        `phase11-smoke-${process.pid}-${Date.now()}.json`
    );
    const smokeChatDir = resolve(
        process.cwd(),
        "data",
        `phase11-chat-${process.pid}-${Date.now()}`
    );

    const previousDataFile = process.env.DATA_FILE;
    const previousChatLogDir = process.env.CHAT_LOG_DIR;
    process.env.DATA_FILE = smokeDataFile;
    process.env.CHAT_LOG_DIR = smokeChatDir;

    const out: string[] = [];
    const err: string[] = [];
    const io = {
        out: (line: string) => out.push(line),
        err: (line: string) => err.push(line)
    };

    try {
        out.length = 0;
        let code = await runCli(["stream:create", "--name", "phase11-stream", "--json"], io);
        assert(code === 0, "Expected stream:create to succeed.");
        const streamResult = parseSingleJson(out);
        const stream = streamResult.stream as { id?: string };
        assert(Boolean(stream?.id), "Expected stream id from stream:create.");

        out.length = 0;
        code = await runCli(
            ["token:create", "--stream", String(stream.id), "--role", "admin", "--json"],
            io
        );
        assert(code === 0, "Expected token:create to succeed.");
        const tokenResult = parseSingleJson(out);
        const token = tokenResult.token as { id?: string; revokedAt?: string | null };
        assert(Boolean(token?.id), "Expected token id from token:create.");
        assert(typeof tokenResult.plainToken === "string", "Expected plainToken from token:create.");

        out.length = 0;
        code = await runCli(["token:list", "--stream", String(stream.id), "--json"], io);
        assert(code === 0, "Expected token:list to succeed.");
        const listBefore = parseSingleJson(out);
        const beforeTokens = listBefore.tokens as Array<{ id?: string; revokedAt?: string | null }>;
        assert(beforeTokens.length === 1, "Expected one token before revoke.");
        assert(beforeTokens[0].revokedAt === null, "Expected token to be active before revoke.");

        out.length = 0;
        code = await runCli(
            [
                "token:rotate",
                "--stream",
                String(stream.id),
                "--role",
                "admin",
                "--old",
                String(token.id),
                "--json"
            ],
            io
        );
        assert(code === 0, "Expected token:rotate to succeed.");
        const rotateResult = parseSingleJson(out);
        const rotatedToken = rotateResult.newToken as { id?: string };
        assert(Boolean(rotatedToken?.id), "Expected new token id from token:rotate.");
        assert(typeof rotateResult.plainToken === "string", "Expected plainToken from token:rotate.");

        out.length = 0;
        code = await runCli(["token:list", "--stream", String(stream.id), "--json"], io);
        assert(code === 0, "Expected token:list after rotate to succeed.");
        const listAfter = parseSingleJson(out);
        const afterTokens = listAfter.tokens as Array<{ id?: string; revokedAt?: string | null }>;
        assert(afterTokens.length === 2, "Expected two tokens after rotation.");
        const oldTokenAfter = afterTokens.find((item) => item.id === token.id);
        const newTokenAfter = afterTokens.find((item) => item.id === rotatedToken.id);
        assert(Boolean(oldTokenAfter?.revokedAt), "Expected old token to be revoked after rotation.");
        assert(newTokenAfter?.revokedAt === null, "Expected new token to be active after rotation.");

        out.length = 0;
        code = await runCli(["token:revoke", "--id", String(rotatedToken.id), "--json"], io);
        assert(code === 0, "Expected token:revoke on rotated token to succeed.");

        console.log(
            JSON.stringify(
                {
                    ok: true,
                    streamId: stream.id,
                    tokenId: token.id,
                    rotatedTokenId: rotatedToken.id,
                    oldRevokedAt: oldTokenAfter?.revokedAt
                },
                null,
                2
            )
        );
    } finally {
        if (previousDataFile === undefined) {
            delete process.env.DATA_FILE;
        } else {
            process.env.DATA_FILE = previousDataFile;
        }
        if (previousChatLogDir === undefined) {
            delete process.env.CHAT_LOG_DIR;
        } else {
            process.env.CHAT_LOG_DIR = previousChatLogDir;
        }
        rmSync(smokeDataFile, { force: true });
        rmSync(smokeChatDir, { force: true, recursive: true });
    }
}

main().catch((error: unknown) => {
    const message = error instanceof Error ? error.message : String(error);
    console.error(message);
    process.exit(1);
});
