import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { mkdtempSync, existsSync, unlinkSync, rmSync } from "node:fs";
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";

describe("voiceprint storage", () => {
  let testDir: string;
  let userId: string;

  beforeEach(() => {
    testDir = mkdtempSync(join(tmpdir(), "vp-storage-test-"));
    userId = "test-user-123";
    vi.spyOn(process, "cwd").mockReturnValue(testDir);
  });

  afterEach(() => {
    vi.restoreAllMocks();
    try {
      rmSync(testDir, { recursive: true, force: true });
    } catch {
      // ignore cleanup errors
    }
  });

  describe("computeHash", () => {
    it("returns a SHA-256 hex string", async () => {
      const { computeHash } = await import("./storage");
      const hash = computeHash(Buffer.from("test-audio"));
      expect(hash).toBeTruthy();
      expect(hash.length).toBe(64);
    });
  });

  describe("saveAudio", () => {
    it("creates directory and saves file", async () => {
      const { saveAudio } = await import("./storage");
      const audioBuffer = Buffer.from("test-audio-content");
      const result = await saveAudio(userId, audioBuffer);

      expect(result.hash).toBeTruthy();
      expect(result.hash.length).toBe(64);
      expect(result.filePath).toContain(userId);
      expect(existsSync(result.filePath)).toBe(true);
    });

    it("reuses existing directory", async () => {
      const { saveAudio } = await import("./storage");
      await saveAudio(userId, Buffer.from("audio-1"));
      await saveAudio(userId, Buffer.from("audio-2"));

      const dir = join(testDir, "uploads", "voiceprint", userId);
      expect(existsSync(dir)).toBe(true);
    });
  });

  describe("getAudioUrl", () => {
    it("returns the audio URL path", async () => {
      const { getAudioUrl } = await import("./storage");
      const url = getAudioUrl("user-1", "some-hash");
      expect(url).toBe("/uploads/voiceprint/user-1/some-hash.wav");
    });
  });

  describe("deleteFile", () => {
    it("deletes a file", async () => {
      const filePath = join(testDir, "test.wav");
      await writeFile(filePath, Buffer.from("test"));
      expect(existsSync(filePath)).toBe(true);

      const { deleteFile } = await import("./storage");
      await deleteFile(filePath);
      expect(existsSync(filePath)).toBe(false);
    });

    it("does not throw if file does not exist", async () => {
      const { deleteFile } = await import("./storage");
      await expect(deleteFile("/nonexistent.wav")).resolves.toBeUndefined();
    });
  });

  describe("deleteAudio", () => {
    it("constructs path and deletes the file", async () => {
      const { saveAudio, deleteAudio } = await import("./storage");
      const { hash } = await saveAudio(userId, Buffer.from("test-audio"));

      const filePath = join(testDir, "uploads", "voiceprint", userId, `${hash}.wav`);
      expect(existsSync(filePath)).toBe(true);

      await deleteAudio(userId, hash);
      expect(existsSync(filePath)).toBe(false);
    });
  });
});
