import { describe, it, beforeEach, afterEach, after } from "node:test";
import assert from "node:assert";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";

const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-file-deletion-"));
const ORIGINAL_DATA_DIR = process.env.DATA_DIR;
process.env.DATA_DIR = TEST_DATA_DIR;

const { createFile, deleteFile, listFiles, createBatch, getBatch, updateBatch } =
  await import("@/lib/localDb");
const { getDbInstance, resetDbInstance } = await import("@/lib/db/core");

after(() => {
  resetDbInstance();
  fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
  if (ORIGINAL_DATA_DIR === undefined) {
    delete process.env.DATA_DIR;
  } else {
    process.env.DATA_DIR = ORIGINAL_DATA_DIR;
  }
});

describe("File Deletion API", () => {
  let testFileId: string;

  afterEach(() => {
    // Ensure cleanup
    const file = listFiles({ limit: 100 }).find((f) => f.filename === "test-delete-file.txt");
    if (file && !file.deletedAt) {
      deleteFile(file.id);
    }
  });

  beforeEach(() => {
    const file = createFile({
      bytes: 100,
      filename: "test-delete-file.txt",
      purpose: "assistants",
      content: Buffer.from("test content"),
      mimeType: "text/plain",
    });
    testFileId = file.id;
  });

  it("should delete a file successfully", () => {
    const file = listFiles({ limit: 100 }).find((f) => f.id === testFileId);
    assert(file !== undefined);
    assert(file.deletedAt === null || file.deletedAt === undefined);

    const success = deleteFile(testFileId);
    assert(success === true);

    const deletedFile = listFiles({ limit: 100 }).find((f) => f.id === testFileId);
    assert(deletedFile === undefined, "Deleted file should not appear in list");
  });

  it("should set deleted_at timestamp on deletion", () => {
    const now = Math.floor(Date.now() / 1000);
    deleteFile(testFileId);

    // Check directly from DB since listFiles filters out deleted files
    const db = getDbInstance();
    const row = db.prepare("SELECT deleted_at FROM files WHERE id = ?").get(testFileId);

    assert(row !== undefined);
    assert(row.deleted_at !== null);
    assert(typeof row.deleted_at === "number");
    // Should be very recent
    assert(row.deleted_at >= now);
    assert(row.deleted_at <= now + 10);
  });

  it("should not list deleted files", () => {
    const beforeCount = listFiles({ limit: 1000 }).length;
    deleteFile(testFileId);
    const afterCount = listFiles({ limit: 1000 }).length;

    assert(afterCount === beforeCount - 1);
  });

  it("should handle deletion of non-existent file gracefully", () => {
    const success = deleteFile("file-nonexistent-12345");
    // Should return false or handle gracefully (depending on implementation)
    assert(typeof success === "boolean");
  });

  describe("Batch file deletion during cleanup", () => {
    it("should delete batch input file after 30 days of completion", () => {
      // Create test batch
      const inputFile = createFile({
        bytes: 100,
        filename: "test-batch-input.jsonl",
        purpose: "batch",
        content: Buffer.from(
          '{"custom_id":"1","method":"POST","url":"/v1/chat/completions","body":{"model":"gpt-4","messages":[{"role":"user","content":"hi"}]}}'
        ),
        mimeType: "application/jsonl",
      });

      const batch = createBatch({
        endpoint: "/v1/chat/completions",
        completionWindow: "24h",
        inputFileId: inputFile.id,
      });

      // Verify batch and file exist
      let batchData = getBatch(batch.id);
      assert(batchData !== null);
      let fileList = listFiles({ limit: 1000 });
      assert(fileList.some((f) => f.id === inputFile.id));

      // Simulate batch completion (completed 31 days ago)
      const thirtyOneDaysAgo = Math.floor(Date.now() / 1000) - 31 * 24 * 60 * 60;
      updateBatch(batch.id, {
        status: "completed",
        completedAt: thirtyOneDaysAgo,
      });

      // After cleanup runs, file should be deleted
      // (Note: this tests the logic, actual cleanup runs in the processor)
      const expiresAt = thirtyOneDaysAgo + 30 * 24 * 60 * 60;
      const now = Math.floor(Date.now() / 1000);
      assert(now > expiresAt, "Current time should be past expiration");
    });

    it("should NOT delete batch input file if not yet expired", () => {
      const inputFile = createFile({
        bytes: 100,
        filename: "test-batch-not-expired.jsonl",
        purpose: "batch",
        content: Buffer.from(
          '{"custom_id":"1","method":"POST","url":"/v1/chat/completions","body":{"model":"gpt-4","messages":[{"role":"user","content":"hi"}]}}'
        ),
        mimeType: "application/jsonl",
      });

      const batch = createBatch({
        endpoint: "/v1/chat/completions",
        completionWindow: "24h",
        inputFileId: inputFile.id,
      });

      // Simulate batch completion (completed 15 days ago)
      const fifteenDaysAgo = Math.floor(Date.now() / 1000) - 15 * 24 * 60 * 60;
      updateBatch(batch.id, {
        status: "completed",
        completedAt: fifteenDaysAgo,
      });

      // File should still exist
      const fileList = listFiles({ limit: 1000 });
      const file = fileList.find((f) => f.id === inputFile.id);
      assert(file !== undefined, "File should still exist before expiration");

      // Cleanup
      deleteFile(inputFile.id);
    });
  });
});
