// Required by Jest: hoisting mocks to the top
jest.mock("fs");
jest.mock("child_process");
jest.mock("@ffmpeg-installer/ffmpeg", () => ({
  path: "mock-ffmpeg-path",
}));
jest.mock("../src/utils", () => {
  const originalModule = jest.requireActual("../src/utils");

  return {
    ...originalModule,
    getVideoMetadata: jest.fn().mockResolvedValue({
      dimensions: {
        width: 1920,
        height: 1080,
      },
      durationInSeconds: 60,
    }),
    waitForFile: jest.fn().mockResolvedValue(true),
    getDeviceIndex: jest.fn().mockResolvedValue(0),
    getCameras: jest
      .fn()
      .mockResolvedValue([{ id: "camera1", name: "FaceTime HD Camera" }]),
    getDisplays: jest
      .fn()
      .mockResolvedValue([{ id: "display1", name: "Main Display" }]),
    getMicrophones: jest
      .fn()
      .mockResolvedValue([{ id: "mic1", name: "Built-in Microphone" }]),
  };
});

import {
  createOutputDirectory,
  getCameras,
  getDeviceIndex,
  getDisplays,
  getMicrophones,
  getVideoMetadata,
  waitForFile,
} from "../src/utils";

describe("Utils functions", () => {
  const fs = require("fs");
  const childProcess = require("child_process");

  beforeEach(() => {
    jest.clearAllMocks();

    // Configure mockFS
    fs.existsSync.mockReturnValue(false);
    fs.mkdirSync.mockReturnValue(undefined);

    // Configure child_process mocks
    childProcess.execFileSync.mockImplementation(
      (command: string, args: string[]) => {
        if (args.includes("-list_devices") && args.includes("true")) {
          return Buffer.from(`
          [AVFoundation input device @ 0x7f8c7f5c5000] AVFoundation video devices:
          [AVFoundation input device @ 0x7f8c7f5c5000] [0] FaceTime HD Camera
          [AVFoundation input device @ 0x7f8c7f5c5000] [1] External Camera
          [AVFoundation input device @ 0x7f8c7f5c5000] AVFoundation audio devices:
          [AVFoundation input device @ 0x7f8c7f5c5000] [0] Built-in Microphone
          [AVFoundation input device @ 0x7f8c7f5c5000] [1] External Microphone
        `);
        }
        return Buffer.from("");
      }
    );
  });

  describe("createOutputDirectory", () => {
    it("should create output directory if it does not exist", async () => {
      // Arrange
      const outputDir = "/test/output";

      // Act
      const result = await createOutputDirectory(outputDir);

      // Assert
      expect(fs.mkdirSync).toHaveBeenCalled();
      expect(result).toBe(outputDir);
    });
  });

  describe("device detection functions", () => {
    it("should return a list of cameras", async () => {
      // Act
      const cameras = await getCameras();

      // Assert
      expect(cameras.length).toBeGreaterThan(0);
      expect(cameras[0]).toHaveProperty("id");
      expect(cameras[0]).toHaveProperty("name");
    });

    it("should return a list of displays", async () => {
      // Act
      const displays = await getDisplays();

      // Assert
      expect(displays.length).toBeGreaterThan(0);
      expect(displays[0]).toHaveProperty("id");
      expect(displays[0]).toHaveProperty("name");
    });

    it("should return a list of microphones", async () => {
      // Act
      const microphones = await getMicrophones();

      // Assert
      expect(microphones.length).toBeGreaterThan(0);
      expect(microphones[0]).toHaveProperty("id");
      expect(microphones[0]).toHaveProperty("name");
    });

    it("should get device index for a display", async () => {
      // Act
      const index = await getDeviceIndex("display");

      // Assert
      expect(index).not.toBeNull();
      expect(typeof index).toBe("number");
    });
  });

  describe("video metadata functions", () => {
    it("should get video metadata", async () => {
      // Act
      const metadata = await getVideoMetadata("/test/video.mp4");

      // Assert
      expect(metadata).toHaveProperty("dimensions");
      expect(metadata).toHaveProperty("durationInSeconds");
    });

    it("should wait for file to exist", async () => {
      // Act
      const result = await waitForFile("/test/video.mp4");

      // Assert
      expect(result).toBe(true);
    });
  });
});
