import { rmSync } from "node:fs";
import { resolve } from "node:path";
import { FileDataStore } from "../db/fileStore";
import { discoverBuiltInExtensions } from "../extensions/builtins";
import { discoverExternalExtensions } from "../extensions/external";
import { ExtensionAuditLog } from "../extensions/auditLog";
import { ExtensionLoader } from "../extensions/loader";
import { ExtensionManifestStore } from "../extensions/manifestStore";
import { attachExtensionAdminController } from "../hub/extensionAdminController";
import { InMemoryEventBus } from "../hub/eventBus";
import { createEvent } from "../hub/events";
import { HubStateRegistry } from "../hub/stateRegistry";

type WsOutRecord = {
    event: string;
    data: Record<string, unknown>;
};

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

function adminCommand(streamId: string, args: string[]) {
    return createEvent("admin_command", {
        streamId,
        tokenId: "phase14-admin-token",
        sessionId: "phase14-session-1",
        command: "ext",
        args,
        actorRole: "admin" as const
    });
}

async function main(): Promise<void> {
    const smokeDataFile = resolve(
        process.cwd(),
        "data",
        `phase14-smoke-${process.pid}-${Date.now()}.json`
    );
    const smokeManifestFile = resolve(
        process.cwd(),
        "data",
        `phase14-manifest-${process.pid}-${Date.now()}.json`
    );
    const smokeAuditFile = resolve(
        process.cwd(),
        "data",
        `phase14-audit-${process.pid}-${Date.now()}.jsonl`
    );
    const smokeChatDir = resolve(
        process.cwd(),
        "data",
        `phase14-chat-${process.pid}-${Date.now()}`
    );
    const store = new FileDataStore(smokeDataFile, {
        chatLogDir: smokeChatDir,
        chatRetentionDays: 30
    });
    const bus = new InMemoryEventBus();
    const stateRegistry = new HubStateRegistry();
    const loader = new ExtensionLoader(bus, stateRegistry, store, () => {});
    const manifestStore = new ExtensionManifestStore(smokeManifestFile);
    const auditLog = new ExtensionAuditLog(smokeAuditFile);
    const wsOut: WsOutRecord[] = [];

    await store.init();
    await manifestStore.load();
    const stream = await store.createStream({ name: "phase14-smoke" });

    bus.on("ws_out", async (event) => {
        wsOut.push({
            event: event.payload.event,
            data: event.payload.data
        });
    });

    attachExtensionAdminController(bus, loader, manifestStore, auditLog, () => {});

    const builtIns = await discoverBuiltInExtensions();
    const externals = await discoverExternalExtensions();
    loader.register(builtIns, "builtin");
    loader.register(externals, "external");

    for (const module of [...builtIns, ...externals]) {
        await loader.enable(module.id);
    }

    const clearWsOut = (): void => {
        wsOut.length = 0;
    };

    try {
        await bus.emit(
            createEvent("chat_message", {
                streamId: stream.id,
                platform: "twitch",
                author: "alice",
                message: "first active message"
            })
        );
        assert(
            wsOut.some((item) => item.event === "merged_chat_message"),
            "Expected merged-chat output before disabling core module."
        );
        const chatBeforeDisable = await store.listChatMessagesByStream(stream.id);
        assert(chatBeforeDisable.length === 1, "Expected merged-chat persistence before disable.");

        clearWsOut();
        await bus.emit(adminCommand(stream.id, ["disable", "merged-chat"]));
        assert(loader.isLoaded("merged-chat"), "Expected merged-chat to remain active.");
        assert(
            wsOut.some((item) => item.event === "extensions_manifest_error"),
            "Expected manifest error when attempting to toggle core module."
        );
        clearWsOut();

        await bus.emit(
            createEvent("chat_message", {
                streamId: stream.id,
                platform: "twitch",
                author: "alice",
                message: "message after denied merged-chat disable"
            })
        );
        assert(
            wsOut.some((item) => item.event === "merged_chat_message"),
            "Expected merged-chat output after denied core module toggle."
        );
        const chatAfterToggleAttempt = await store.listChatMessagesByStream(stream.id);
        assert(
            chatAfterToggleAttempt.length === 2,
            "Expected merged-chat persistence to continue after denied core module toggle."
        );

        clearWsOut();
        await bus.emit(
            createEvent("chat_command", {
                streamId: stream.id,
                command: "poll",
                args: ["start", "Toggle", "works?"],
                source: "chat",
                origin: "admin",
                actor: {
                    role: "admin",
                    platform: "local",
                    userId: "phase14-admin-token"
                }
            })
        );
        assert(wsOut.some((item) => item.event === "poll_update"), "Expected poll_update before disable.");

        clearWsOut();
        await bus.emit(adminCommand(stream.id, ["disable", "polls"]));
        assert(!loader.isLoaded("polls"), "Expected polls to be disabled.");
        clearWsOut();

        await bus.emit(
            createEvent("chat_command", {
                streamId: stream.id,
                command: "poll",
                args: ["status"],
                source: "chat",
                origin: "chat",
                actor: {
                    role: "viewer",
                    platform: "twitch",
                    username: "alice"
                }
            })
        );
        assert(
            !wsOut.some((item) => item.event === "poll_update"),
            "Did not expect poll_update after disabling external extension."
        );

        clearWsOut();
        await bus.emit(adminCommand(stream.id, ["enable", "polls"]));
        assert(loader.isLoaded("polls"), "Expected polls to be re-enabled.");
        clearWsOut();

        await bus.emit(
            createEvent("chat_command", {
                streamId: stream.id,
                command: "poll",
                args: ["status"],
                source: "chat",
                origin: "chat",
                actor: {
                    role: "viewer",
                    platform: "twitch",
                    username: "alice"
                }
            })
        );
        assert(
            wsOut.some((item) => item.event === "poll_update"),
            "Expected poll_update after re-enabling external extension."
        );

        console.log(
            JSON.stringify(
                {
                    ok: true,
                    verified: {
                        coreModuleToggleDenied: "merged-chat",
                        externalExtensionToggling: "polls"
                    }
                },
                null,
                2
            )
        );
    } finally {
        rmSync(smokeDataFile, { force: true });
        rmSync(smokeChatDir, { force: true, recursive: true });
        rmSync(smokeManifestFile, { force: true });
        rmSync(smokeAuditFile, { force: true });
    }
}

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