import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { mkdtempSync, rmSync, existsSync, readFileSync, statSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { ensureSecureDir, writeSecureFile, appendSecureFile } from "./fs-permissions.js";

let tempDir: string;

beforeEach(() => {
  tempDir = mkdtempSync(join(tmpdir(), "fs-perms-test-"));
});

afterEach(() => {
  rmSync(tempDir, { recursive: true, force: true });
});

describe("ensureSecureDir", () => {
  it("creates a directory recursively", () => {
    const nested = join(tempDir, "a", "b", "c");
    ensureSecureDir(nested);
    expect(existsSync(nested)).toBe(true);
  });

  it("is idempotent — calling twice does not throw", () => {
    const dir = join(tempDir, "existing");
    ensureSecureDir(dir);
    expect(() => ensureSecureDir(dir)).not.toThrow();
  });

  it("sets mode 0700 on Unix", () => {
    if (process.platform === "win32") return; // chmod is a no-op on Windows
    const dir = join(tempDir, "secure");
    ensureSecureDir(dir);
    const mode = statSync(dir).mode & 0o777;
    expect(mode).toBe(0o700);
  });
});

describe("writeSecureFile", () => {
  it("writes file contents", () => {
    const filePath = join(tempDir, "test.json");
    writeSecureFile(filePath, '{"key":"value"}');
    expect(readFileSync(filePath, "utf-8")).toBe('{"key":"value"}');
  });

  it("overwrites existing file", () => {
    const filePath = join(tempDir, "test.txt");
    writeSecureFile(filePath, "first");
    writeSecureFile(filePath, "second");
    expect(readFileSync(filePath, "utf-8")).toBe("second");
  });

  it("sets mode 0600 on Unix", () => {
    if (process.platform === "win32") return;
    const filePath = join(tempDir, "secret.json");
    writeSecureFile(filePath, "data");
    const mode = statSync(filePath).mode & 0o777;
    expect(mode).toBe(0o600);
  });
});

describe("appendSecureFile", () => {
  it("creates file on first append", () => {
    const filePath = join(tempDir, "log.jsonl");
    appendSecureFile(filePath, "line1\n");
    expect(readFileSync(filePath, "utf-8")).toBe("line1\n");
  });

  it("appends to existing file", () => {
    const filePath = join(tempDir, "log.jsonl");
    appendSecureFile(filePath, "line1\n");
    appendSecureFile(filePath, "line2\n");
    expect(readFileSync(filePath, "utf-8")).toBe("line1\nline2\n");
  });

  it("sets mode 0600 on Unix", () => {
    if (process.platform === "win32") return;
    const filePath = join(tempDir, "log.jsonl");
    appendSecureFile(filePath, "data\n");
    const mode = statSync(filePath).mode & 0o777;
    expect(mode).toBe(0o600);
  });
});
