"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ScreenRecorder = void 0;
const node_path_1 = __importDefault(require("node:path"));
const node_fs_1 = __importDefault(require("node:fs"));
const ffmpeg_1 = __importDefault(require("@ffmpeg-installer/ffmpeg"));
const node_child_process_1 = require("node:child_process");
const Logger_1 = require("./Logger");
const IocdWdioServiceError_1 = require("../../utils/error/IocdWdioServiceError");
const Paths_1 = require("./Paths");
const MIN_RECORDING_MS = 1000;
/**
 * A Windows-only utility class responsible for full-display screen recording during IOCD test runs.
 *
 * It wraps an ffmpeg subprocess using the `gdigrab` input device to capture the entire desktop.
 *
 * Behavior:
 * - `start()` spawns ffmpeg and begins recording.
 * - `stop()` gracefully terminates ffmpeg and finalizes the video file.
 * - Automatically ensures output directories exist.
 * - Can delete recordings for passed tests to keep artifacts clean.
 *
 * Usage:
 * ```ts
 * const recorder = new ScreenRecorder({ outDir: 'D:\DEV\desktop\wdio-tests\artifacts\videos' });
 * await recorder.start('my-test-name');
 * await recorder.stop(true);
 * ```
 */
class ScreenRecorder {
    constructor(options) {
        this.logger = (0, Logger_1.Logger)('ScreenRecorder');
        this.fps = options.fps;
        this.outDir = options?.outDir;
        this.ffmpegPath = ffmpeg_1.default.path;
        Paths_1.Paths.ensureDirExists(this.outDir);
        if (process.platform !== 'win32') {
            this.logger.error('Only Windows OS is supported for screen recording of failing tests.');
        }
    }
    /**
     * Builds the ffmpeg CLI arguments for full desktop capture on Windows using the `gdigrab` device.
     *
     * Notes:
     * - The configuration favors speed and stability over compression efficiency.
     * - It includes low-latency tuning and ensures the output file is playable even if recording stops abruptly.
     * - Works best with `.mkv` output (instant header finalization), but also fine with `.mp4` due to `+faststart`.
     */
    buildFfmpegArgs(outFile) {
        return [
            '-y', // Overwrite output file without confirmation
            '-f',
            'gdigrab', // Use Windows GDI screen capture input (captures entire desktop)
            '-draw_mouse',
            '1', // Include the mouse cursor in the recording
            '-framerate',
            String(this.fps), // Capture frame rate (e.g. 25 or 30 FPS)
            // Input source — "desktop" captures the full primary screen.
            // When running over RDP, can be replaced with 'title=<window title>' to target a specific window.
            '-i',
            'desktop',
            '-vcodec',
            'libx264', // Use H.264 video codec for broad compatibility
            '-preset',
            'ultrafast', // Minimize CPU usage and latency; increases file size slightly
            '-pix_fmt',
            'yuv420p', // Standard pixel format ensuring player compatibility (e.g. VLC, browsers)
            '-movflags',
            '+faststart', // Write file headers early so playback starts immediately in MP4 containers
            '-tune',
            'zerolatency', // Disable buffering — ensures frames are flushed as they’re encoded
            outFile, // Absolute output file path (.mkv or .mp4)
        ];
    }
    /**
     * Starts an ffmpeg subprocess to record the full Windows display.
     * Uses .mkv container for safer short recordings (headers are written immediately).
     * @param testName - Name of the current test (used in output filename)
     */
    async start(testName) {
        if (this.process) {
            this.logger.warn('Screen recorder already running.');
        }
        const safeName = testName.replace(/[<>:"/\\|?*\x00-\x1F]/g, '_');
        // MKV is safer if test ends quickly; switch to mp4 later if needed
        this.outputFile = node_path_1.default.join(this.outDir, `${safeName}.mkv`);
        const args = this.buildFfmpegArgs(this.outputFile);
        this.process = (0, node_child_process_1.spawn)(this.ffmpegPath, args, { stdio: ['pipe', 'pipe', 'pipe'] });
        this.process.stderr?.on('data', data => {
            const line = data.toString();
            // Optionally parse fps/frame count for debugging
            if (line.includes('frame='))
                this.logger.debug(`[ffmpeg] ${line.trim()}`);
        });
        this.process.on('error', err => {
            throw new IocdWdioServiceError_1.IocdWdioServiceError(`Failed to start ffmpeg: ${err.message}`);
        });
        this.logger.info(`[IOCD] Screen recording started → ${this.outputFile}`);
    }
    /**
     * Stops the ffmpeg subprocess and optionally deletes the file.
     * @param keep - Whether to keep the file (true for failed tests, false for passed).
     * @returns The absolute path to the saved file, or null if deleted.
     */
    async stop(keep) {
        if (!this.process || !this.outputFile)
            return null;
        // wait a bit so ffmpeg can grab at least one frame
        await new Promise(res => setTimeout(res, MIN_RECORDING_MS));
        return new Promise(resolve => {
            this.process.on('close', () => {
                const file = this.outputFile;
                if (!keep && node_fs_1.default.existsSync(file)) {
                    node_fs_1.default.unlinkSync(file);
                    this.logger.info(`Deleted recording (test passed): ${file}`);
                    resolve(null);
                    return;
                }
                this.logger.info(`Saved screen recording: ${file}`);
                resolve(file);
            });
            try {
                this.process.stdin?.write('q');
                this.process.stdin?.end();
            }
            catch {
                this.process.kill('SIGINT');
            }
            // fallback kill if ffmpeg doesn't exit
            setTimeout(() => {
                if (!this.process.killed)
                    this.process.kill('SIGINT');
            }, 2000);
        });
    }
}
exports.ScreenRecorder = ScreenRecorder;
