/**
 * Contents.CN Chunk 下載整合測試
 *
 * 測試大檔 chunk 下載的完整流程
 */

import { test, expect, describe, beforeEach, afterEach } from "bun:test";
import { mkdirSync, existsSync, writeFileSync, rmSync } from "node:fs";
import { join } from "node:path";
import {
  shouldUseChunkedDownload,
  getChunkDownloadInfo,
  getChunk,
  createChunkDownloadHeaders,
} from "../../src/transfer/downloadManager";
import { CHUNK_SIZE_BYTES } from "../../src/transfer/constants";

const testDir = "./test-output-chunk-download";

describe("Chunk 下載資訊測試", () => {
  beforeEach(() => {
    if (!existsSync(testDir)) {
      mkdirSync(testDir, { recursive: true });
    }
  });

  afterEach(() => {
    if (existsSync(testDir)) {
      rmSync(testDir, { recursive: true, force: true });
    }
  });

  test("getChunkDownloadInfo 應返回正確的下載資訊", () => {
    const testFile = join(testDir, "large-file.bin");

    // 建立一個假檔案（只寫入少量資料模擬）
    const content = Buffer.alloc(1024, "X"); // 1KB
    writeFileSync(testFile, content);

    const info = getChunkDownloadInfo(testFile);

    expect(info).not.toBeNull();
    expect(info!.file_name).toBe("large-file.bin");
    expect(info!.total_size).toBe(1024);
    expect(info!.chunk_size).toBe(CHUNK_SIZE_BYTES);
    expect(info!.total_chunks).toBe(1); // 1KB 只需要 1 chunk
  });

  test("不存在的檔案應返回 null", () => {
    const info = getChunkDownloadInfo(join(testDir, "non-existent.bin"));
    expect(info).toBeNull();
  });
});

describe("Chunk 讀取測試", () => {
  beforeEach(() => {
    if (!existsSync(testDir)) {
      mkdirSync(testDir, { recursive: true });
    }
  });

  afterEach(() => {
    if (existsSync(testDir)) {
      rmSync(testDir, { recursive: true, force: true });
    }
  });

  test("getChunk 應正確讀取指定的 chunk", async () => {
    const testFile = join(testDir, "chunked-file.bin");

    // 建立測試檔案：20 bytes，分成 4 個 5-byte chunks
    const content = "AAAAABBBBBCCCCCDDDD"; // 19 bytes
    writeFileSync(testFile, content);

    // 讀取第一個 chunk（5 bytes）
    const chunk0 = await getChunk(testFile, 0, 5);
    expect(chunk0!.toString()).toBe("AAAAA");

    // 讀取第二個 chunk
    const chunk1 = await getChunk(testFile, 1, 5);
    expect(chunk1!.toString()).toBe("BBBBB");

    // 讀取第三個 chunk
    const chunk2 = await getChunk(testFile, 2, 5);
    expect(chunk2!.toString()).toBe("CCCCC");

    // 讀取最後一個 chunk（只有 4 bytes）
    const chunk3 = await getChunk(testFile, 3, 5);
    expect(chunk3!.toString()).toBe("DDDD");
  });

  test("讀取超出範圍的 chunk 應返回 null", async () => {
    const testFile = join(testDir, "small-file.txt");
    writeFileSync(testFile, "Hello");

    const chunk = await getChunk(testFile, 10, 5); // 超出範圍
    expect(chunk).toBeNull();
  });

  test("不存在的檔案應返回 null", async () => {
    const chunk = await getChunk(join(testDir, "non-existent.bin"), 0, 5);
    expect(chunk).toBeNull();
  });
});

describe("Chunk 下載 Headers 測試", () => {
  test("createChunkDownloadHeaders 應生成正確的 headers", () => {
    const info = {
      total_size: 1000,
      total_chunks: 4,
      chunk_size: 256,
      file_name: "test-file.bin",
    };

    const chunkData = Buffer.alloc(256, "X");
    const headers = createChunkDownloadHeaders(info, 0, chunkData);

    expect(headers["Content-Type"]).toBe("application/octet-stream");
    expect(headers["Content-Length"]).toBe("256");
    expect(headers["Content-Range"]).toBe("bytes 0-255/1000");
    expect(headers["Accept-Ranges"]).toBe("bytes");
    expect(headers["X-Chunk-Index"]).toBe("0");
    expect(headers["X-Total-Chunks"]).toBe("4");
    expect(headers["X-Total-Size"]).toBe("1000");
  });

  test("最後一個 chunk 的 headers 應正確", () => {
    const info = {
      total_size: 1000,
      total_chunks: 4,
      chunk_size: 256,
      file_name: "test-file.bin",
    };

    // 最後一個 chunk 只有 232 bytes (1000 - 256*3)
    const chunkData = Buffer.alloc(232, "Z");
    const headers = createChunkDownloadHeaders(info, 3, chunkData);

    expect(headers["Content-Length"]).toBe("232");
    expect(headers["Content-Range"]).toBe("bytes 768-999/1000");
    expect(headers["X-Chunk-Index"]).toBe("3");
  });
});

describe("傳輸模式判斷測試", () => {
  beforeEach(() => {
    if (!existsSync(testDir)) {
      mkdirSync(testDir, { recursive: true });
    }
  });

  afterEach(() => {
    if (existsSync(testDir)) {
      rmSync(testDir, { recursive: true, force: true });
    }
  });

  test("小檔（≤10MB）不應使用 chunk 下載", () => {
    const smallFile = join(testDir, "small.txt");
    writeFileSync(smallFile, "Small file content");

    expect(shouldUseChunkedDownload(smallFile)).toBe(false);
  });

  test("不存在的檔案不應使用 chunk 下載", () => {
    expect(shouldUseChunkedDownload(join(testDir, "non-existent.bin"))).toBe(false);
  });
});

describe("完整 chunk 下載流程測試", () => {
  beforeEach(() => {
    if (!existsSync(testDir)) {
      mkdirSync(testDir, { recursive: true });
    }
  });

  afterEach(() => {
    if (existsSync(testDir)) {
      rmSync(testDir, { recursive: true, force: true });
    }
  });

  test("應能完整讀取並合併所有 chunks", async () => {
    const testFile = join(testDir, "complete-file.bin");
    const originalContent = "AAAAAABBBBBBCCCCCCDDDDDDEEEEEE"; // 30 bytes
    writeFileSync(testFile, originalContent);

    const info = getChunkDownloadInfo(testFile);
    expect(info).not.toBeNull();

    // 模擬分段讀取（每個 chunk 10 bytes）
    const chunkSize = 10;
    const totalChunks = Math.ceil(originalContent.length / chunkSize);
    const chunks: Buffer[] = [];

    for (let i = 0; i < totalChunks; i++) {
      const chunk = await getChunk(testFile, i, chunkSize);
      if (chunk) {
        chunks.push(chunk);
      }
    }

    // 合併所有 chunks
    const mergedContent = Buffer.concat(chunks).toString();
    expect(mergedContent).toBe(originalContent);
  });
});
