import { InMemoryEventBus } from "../hub/eventBus";
import { startYouTubeAdapter, type YouTubeAdapterStatus } from "../platform/youtube";

function delay(ms: number): Promise<void> {
    return new Promise((resolveDelay) => setTimeout(resolveDelay, ms));
}

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

async function main(): Promise<void> {
    const bus = new InMemoryEventBus();
    const statuses: YouTubeAdapterStatus[] = [];
    let tokenCalls = 0;
    let liveBroadcastCalls = 0;
    let streamCalls = 0;
    const liveBroadcastQueries: URLSearchParams[] = [];

    const fakeFetch: typeof fetch = async (input, init) => {
        const url =
            typeof input === "string" ? new URL(input) : input instanceof URL ? input : new URL(input.url);

        if (url.hostname === "oauth2.googleapis.com" && url.pathname === "/token") {
            tokenCalls += 1;
            return {
                ok: true,
                status: 200,
                statusText: "OK",
                json: async () => ({
                    access_token: "oauth-phase20-token",
                    expires_in: 300
                })
            } as Response;
        }

        if (url.pathname.endsWith("/liveBroadcasts")) {
            liveBroadcastCalls += 1;
            liveBroadcastQueries.push(url.searchParams);
            return {
                ok: true,
                status: 200,
                statusText: "OK",
                json: async () => ({
                    items: [{ snippet: { liveChatId: "phase20-live-chat-id" } }]
                })
            } as Response;
        }

        throw new Error(`Unexpected URL in fakeFetch: ${url.toString()}`);
    };
    const fakeStreamList = async function* () {
        streamCalls += 1;
        yield {
            nextPageToken: "next",
            items: []
        };
    };

    const adapter = startYouTubeAdapter({
        eventBus: bus,
        apiKey: "fake-key",
        oauthClientId: "client-id",
        oauthClientSecret: "client-secret",
        oauthRefreshToken: "refresh-token",
        pollMs: 10,
        discoveryPollMs: 10,
        errorLogCooldownMs: 2000,
        fetchImpl: fakeFetch,
        streamListImpl: fakeStreamList,
        onStatusChanged: (status) => statuses.push(status),
        log: () => {}
    });

    try {
        await delay(40);
        assert(liveBroadcastCalls === 0, "Expected no discovery while discovery is paused.");
        assert(
            statuses.some(
                (status) =>
                    status.adminPresenceActive === false &&
                    status.state === "paused" &&
                    status.lastDiscoveryResult === "idle" &&
                    status.currentLiveChatId === null
            ),
            "Expected initial paused status with adminPresenceActive=false."
        );

        adapter.setAdminPresenceActive(true);
        adapter.armDiscovery();
        await delay(80);

        assert(tokenCalls >= 1, "Expected oauth token refresh once discovery was armed.");
        assert(liveBroadcastCalls >= 1, "Expected discovery call after arming.");
        assert(
            liveBroadcastQueries.every(
                (params) =>
                    params.get("part") === "snippet" &&
                    params.get("fields") === "items/snippet/liveChatId" &&
                    params.get("mine") === "true"
            ),
            "Expected liveBroadcasts discovery calls to request only the required snippet liveChatId fields."
        );
        assert(streamCalls >= 1, "Expected live chat streaming after discovery.");
        assert(
            statuses.some(
                (status) => status.adminPresenceActive === true && status.lastDiscoveryResult === "idle"
            ),
            "Expected status update when admin presence becomes active."
        );
        assert(
            statuses.some(
                (status) =>
                    status.state === "connected" &&
                    status.lastDiscoveryResult === "found" &&
                    status.currentLiveChatId === "phase20-live-chat-id" &&
                    status.mode === "auto"
            ),
            "Expected discovered status update to include current liveChatId."
        );

        console.log(
            JSON.stringify(
                {
                    ok: true,
                    tokenCalls,
                    liveBroadcastCalls,
                    streamCalls,
                    statusUpdates: statuses.length
                },
                null,
                2
            )
        );
    } finally {
        adapter.stop();
    }
}

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