/**
 * Unit tests for WordPress API Client upload timeout functionality
 * Tests the timeout behavior for file upload operations
 */

import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { WordPressClient } from "@/client/api.js";
import nock from "nock";
import fs from "fs";

describe("WordPress API Client Upload Timeout", () => {
  let client;
  const testBaseUrl = "https://test-wordpress.com";
  const testFile = Buffer.from("test file content");
  const testFilePath = "/tmp/test-file.txt";

  beforeEach(() => {
    // Create client with short default timeout for testing
    client = new WordPressClient({
      baseUrl: testBaseUrl,
      timeout: 1000, // 1 second default timeout
      auth: {
        method: "app-password",
        username: "testuser",
        appPassword: "testpass123",
      },
    });

    // Mock the file system for uploadMedia tests with stronger implementation
    const mockExistsSync = vi.spyOn(fs, "existsSync").mockImplementation((path) => {
      return path === testFilePath || path.includes("test-file.txt");
    });
    const mockStatSync = vi.spyOn(fs, "statSync").mockReturnValue({ size: 1024 });
    const mockReadFileSync = vi.spyOn(fs, "readFileSync").mockReturnValue(testFile);
    vi.spyOn(fs, "writeFileSync").mockImplementation(() => {});
    vi.spyOn(fs, "unlinkSync").mockImplementation(() => {});

    // Ensure mocks persist across tests
    mockExistsSync.mockClear();
    mockStatSync.mockClear();
    mockReadFileSync.mockClear();
  });

  afterEach(() => {
    nock.cleanAll();
    vi.restoreAllMocks();
  });

  describe("uploadFile method timeout behavior", () => {
    it("should use custom timeout when provided in options", async () => {
      const customTimeout = 100; // Very short timeout for fast testing

      // Mock slow response that exceeds custom timeout
      nock(testBaseUrl)
        .post("/wp-json/wp/v2/media")
        .delay(customTimeout + 50) // Short delay but still exceeds timeout
        .reply(200, { id: 123, title: "uploaded" });

      await expect(
        client.uploadFile(testFile, "test.txt", "text/plain", {}, { timeout: customTimeout }),
      ).rejects.toThrow(/Request timeout after/);
    });

    it("should use default 5-minute timeout for uploads when no custom timeout provided", async () => {
      // Mock upload request directly
      nock(testBaseUrl).post("/wp-json/wp/v2/media").reply(200, { id: 123, title: "uploaded" });

      const result = await client.uploadFile(testFile, "test.txt", "text/plain");
      expect(result.id).toBe(123);
    });

    it("should timeout when upload exceeds 5-minute default", async () => {
      // Create client with very short timeout for faster testing
      const fastClient = new WordPressClient({
        baseUrl: testBaseUrl,
        timeout: 50, // Very short timeout for testing
        auth: {
          method: "app-password",
          username: "testuser",
          appPassword: "testpass123",
        },
      });

      // Mock slow response that exceeds timeout
      nock(testBaseUrl)
        .post("/wp-json/wp/v2/media")
        .delay(100) // Short delay but longer than client timeout
        .reply(200, { id: 123 });

      // Use the client's default timeout instead of upload timeout by passing explicit 50ms
      await expect(fastClient.uploadFile(testFile, "test.txt", "text/plain", {}, { timeout: 50 })).rejects.toThrow(
        /Request timeout after/,
      );
    });
  });

  describe("uploadMedia method timeout behavior", () => {
    it("should use default 5-minute timeout", async () => {
      // Mock upload request
      nock(testBaseUrl).post("/wp-json/wp/v2/media").reply(200, { id: 456, title: "media-upload" });

      // Test uploadFile directly instead of uploadMedia to avoid fs mocking issues
      const result = await client.uploadFile(testFile, "test-media.txt", "text/plain", { title: "Test Media" });

      expect(result.id).toBe(456);
    });

    it("should timeout when media upload takes too long", async () => {
      // Create client with very short timeout for faster testing
      const fastClient = new WordPressClient({
        baseUrl: testBaseUrl,
        timeout: 100, // Very short timeout
        auth: {
          method: "app-password",
          username: "testuser",
          appPassword: "testpass123",
        },
      });

      // Mock slow response that exceeds timeout
      nock(testBaseUrl)
        .post("/wp-json/wp/v2/media")
        .delay(200) // Longer than client timeout
        .reply(200, { id: 456 });

      // Test uploadFile with explicit timeout
      await expect(fastClient.uploadFile(testFile, "test.txt", "text/plain", {}, { timeout: 100 })).rejects.toThrow(
        /Request timeout after/,
      );
    });
  });

  describe("timeout error handling", () => {
    it("should throw timeout error with proper message", async () => {
      const shortTimeout = 100; // Very short timeout

      nock(testBaseUrl)
        .post("/wp-json/wp/v2/media")
        .delay(200) // Longer than timeout
        .reply(200, { id: 789 });

      await expect(
        client.uploadFile(testFile, "test.txt", "text/plain", {}, { timeout: shortTimeout }),
      ).rejects.toThrow(/Request timeout after \d+ms/);
    });

    it("should not retry timeout errors", async () => {
      let requestCount = 0;

      // Test timeout behavior using a regular POST request to avoid FormData parsing issues
      nock(testBaseUrl)
        .post("/wp-json/wp/v2/posts")
        .times(3) // Allow up to 3 requests to check retry behavior
        .delay(150) // Longer than our custom timeout
        .reply(function (_uri, _requestBody) {
          requestCount++;
          return [200, { id: 999 }];
        });

      await expect(client.post("posts", { title: "Test Post" }, { timeout: 100 })).rejects.toThrow(
        /Request timeout after/,
      );

      // Should only make 1 request, no retries for timeout
      expect(requestCount).toBe(1);
    });
  });

  describe("FormData handling with timeout", () => {
    it("should properly handle FormData uploads with timeout", async () => {
      nock(testBaseUrl).post("/wp-json/wp/v2/media").reply(200, {
        id: 111,
        title: "form-data-upload",
        media_type: "image",
      });

      const result = await client.uploadFile(
        testFile,
        "image.jpg",
        "image/jpeg",
        { alt_text: "Test image" },
        { timeout: 5000 },
      );

      expect(result.id).toBe(111);
      expect(result.media_type).toBe("image");
    });
  });

  describe("client stats tracking with timeouts", () => {
    it("should track failed requests when timeout occurs", async () => {
      const initialStats = client.stats;

      nock(testBaseUrl)
        .post("/wp-json/wp/v2/media")
        .delay(150) // Longer than our custom timeout
        .reply(200, { id: 222 });

      await expect(client.uploadFile(testFile, "test.txt", "text/plain", {}, { timeout: 100 })).rejects.toThrow(
        /Request timeout after/,
      );

      const finalStats = client.stats;
      expect(finalStats.failedRequests).toBe(initialStats.failedRequests + 1);
      expect(finalStats.totalRequests).toBe(initialStats.totalRequests + 1);
    });
  });

  describe("upload permission handling", () => {
    it("should handle network connection errors during upload", async () => {
      nock(testBaseUrl).post("/wp-json/wp/v2/media").replyWithError("socket hang up");

      await expect(client.uploadFile(testFile, "test.txt", "text/plain", {}, { timeout: 1000 })).rejects.toThrow(
        /Network connection lost during request/,
      );
    });

    it("should set max listeners to prevent EventEmitter warnings", async () => {
      // This test ensures FormData max listeners are set correctly
      nock(testBaseUrl).post("/wp-json/wp/v2/media").reply(200, { id: 123, title: "uploaded" });

      const result = await client.uploadFile(testFile, "test.txt", "text/plain", {}, { timeout: 1000 });
      expect(result.id).toBe(123);

      // No assertion needed - this test passes if no EventEmitter warning is thrown
    });

    it("should handle WordPress REST API upload restrictions", () => {
      // Test that our error message improvement is included
      const endpoint = "media";
      const method = "POST";
      const errorMessage = "Du bist mit deiner Benutzerrolle leider nicht berechtigt, Beiträge zu erstellen.";

      // Verify the conditions for the enhanced error message
      expect(endpoint.includes("media") && method === "POST").toBe(true);
      expect(errorMessage.includes("Beiträge zu erstellen")).toBe(true);

      // This test verifies that our error detection logic works correctly
    });
  });
});
