import { describe, it, expect, jest, beforeEach, afterEach } from "@jest/globals";
import { EventEmitter } from "events";
// Mock fs before imports
const mockExistsSync = jest.fn();
jest.unstable_mockModule("fs", ()=>({
        existsSync: mockExistsSync,
        default: {
            existsSync: mockExistsSync
        }
    }));
// Mock CredentialsManager
const mockGetCloudflareCredentials = jest.fn();
const mockSetCloudflareCredentials = jest.fn();
jest.unstable_mockModule("../../utils/credentials-no-keytar.js", ()=>({
        CredentialsManager: {
            getCloudflareCredentials: mockGetCloudflareCredentials,
            setCloudflareCredentials: mockSetCloudflareCredentials
        }
    }));
// Mock child_process
const mockSpawn = jest.fn();
jest.unstable_mockModule("child_process", ()=>({
        spawn: mockSpawn,
        ChildProcess: EventEmitter
    }));
// Mock node-fetch
const mockFetch = jest.fn();
jest.unstable_mockModule("node-fetch", ()=>({
        default: mockFetch
    }));
// Import after mocks
const { CloudflareTunnelManager } = await import("../cloudflare-tunnel.js");
describe("CloudflareTunnelManager", ()=>{
    let mockAppState;
    let tunnelManager;
    beforeEach(()=>{
        jest.clearAllMocks();
        jest.useFakeTimers();
        mockAppState = {
            getTunnelConfig: jest.fn().mockResolvedValue(null),
            saveTunnelConfig: jest.fn().mockResolvedValue(undefined),
            clearTunnelConfig: jest.fn().mockResolvedValue(undefined)
        };
        mockGetCloudflareCredentials.mockResolvedValue({
            apiToken: "test-token",
            accountId: "test-account",
            zoneId: "test-zone"
        });
        tunnelManager = new CloudflareTunnelManager(mockAppState);
    });
    afterEach(()=>{
        jest.useRealTimers();
    });
    describe("getStatus", ()=>{
        it("should return initial status as inactive", ()=>{
            const status = tunnelManager.getStatus();
            expect(status).toEqual({
                active: false,
                url: null,
                error: null
            });
        });
    });
    describe("start", ()=>{
        it("should throw when cloudflared binary is not found", async ()=>{
            mockExistsSync.mockReturnValue(false);
            await expect(tunnelManager.start()).rejects.toThrow("cloudflared binary not found");
        });
        it("should throw when credentials are not configured", async ()=>{
            mockExistsSync.mockReturnValue(true);
            mockGetCloudflareCredentials.mockResolvedValue(null);
            await expect(tunnelManager.start()).rejects.toThrow("Cloudflare credentials not found");
        });
        it("should create tunnel and start process with valid config", async ()=>{
            mockExistsSync.mockReturnValue(true);
            // Mock existing tunnel config
            mockAppState.getTunnelConfig.mockResolvedValue({
                id: "existing-tunnel-id",
                token: "existing-token",
                hostname: "test.streamline.club"
            });
            // Create mock child process
            const mockProcess = new EventEmitter();
            mockProcess.stdout = new EventEmitter();
            mockProcess.stderr = new EventEmitter();
            mockProcess.kill = jest.fn().mockReturnValue(true);
            mockSpawn.mockReturnValue(mockProcess);
            // Mock health check
            mockFetch.mockResolvedValue({
                ok: true
            });
            // Start tunnel (non-blocking)
            const startPromise = tunnelManager.start();
            // Simulate process output indicating connection
            await Promise.resolve(); // Let event loop tick
            mockProcess.stdout.emit("data", Buffer.from("Connection registered"));
            // Advance timers to let waitForTunnelReady complete
            await jest.advanceTimersByTimeAsync(100);
            await startPromise;
            expect(mockSpawn).toHaveBeenCalled();
            expect(tunnelManager.getStatus().active).toBe(true);
            expect(tunnelManager.getStatus().url).toBe("https://test.streamline.club");
        });
    });
    describe("stop", ()=>{
        it("should resolve without error when no tunnel is running", async ()=>{
            await expect(tunnelManager.stop()).resolves.toBeUndefined();
        });
        it("should kill the process when tunnel is running", async ()=>{
            mockExistsSync.mockReturnValue(true);
            mockAppState.getTunnelConfig.mockResolvedValue({
                id: "test-tunnel",
                token: "test-token",
                hostname: "test.streamline.club"
            });
            const mockProcess = new EventEmitter();
            mockProcess.stdout = new EventEmitter();
            mockProcess.stderr = new EventEmitter();
            mockProcess.kill = jest.fn().mockImplementation(()=>{
                // Emit exit after a small delay to allow stop() to set up listener
                setImmediate(()=>mockProcess.emit("exit", 0));
                return true;
            });
            mockProcess.pid = 12345;
            mockSpawn.mockReturnValue(mockProcess);
            mockFetch.mockResolvedValue({
                ok: true
            });
            // Start tunnel
            const startPromise = tunnelManager.start();
            await Promise.resolve();
            mockProcess.stdout.emit("data", Buffer.from("Connection registered"));
            await jest.advanceTimersByTimeAsync(100);
            await startPromise;
            // Stop tunnel
            const stopPromise = tunnelManager.stop();
            await jest.advanceTimersByTimeAsync(100);
            await stopPromise;
            expect(mockProcess.kill).toHaveBeenCalled();
        });
    });
    describe("status changes", ()=>{
        it("should update internal status when tunnel starts", async ()=>{
            mockExistsSync.mockReturnValue(true);
            mockAppState.getTunnelConfig.mockResolvedValue({
                id: "test-tunnel",
                token: "test-token",
                hostname: "test.streamline.club"
            });
            const mockProcess = new EventEmitter();
            mockProcess.stdout = new EventEmitter();
            mockProcess.stderr = new EventEmitter();
            mockProcess.kill = jest.fn();
            mockSpawn.mockReturnValue(mockProcess);
            mockFetch.mockResolvedValue({
                ok: true
            });
            // Initially inactive
            expect(tunnelManager.getStatus().active).toBe(false);
            // Start tunnel
            const startPromise = tunnelManager.start();
            await Promise.resolve();
            mockProcess.stdout.emit("data", Buffer.from("Connection registered"));
            await jest.advanceTimersByTimeAsync(100);
            await startPromise;
            // Now active
            expect(tunnelManager.getStatus().active).toBe(true);
            expect(tunnelManager.getStatus().url).toBe("https://test.streamline.club");
        });
    });
});

//# sourceMappingURL=cloudflare-tunnel.test.js.map