/**
 * Comprehensive tests for CommentTools class
 * Achieving ≥70% coverage for comments tool implementation
 */

import { vi } from "vitest";

// Mock the dependencies
vi.mock("../../../dist/client/api.js");

// Now import the modules after mocking
const { CommentTools } = await import("../../../dist/tools/comments.js");
const { WordPressClient } = await import("../../../dist/client/api.js"); // eslint-disable-line no-unused-vars

describe("CommentTools", () => {
  let commentTools;
  let mockClient;

  beforeEach(() => {
    // Reset mocks
    vi.clearAllMocks();

    // Create mock client instance with all necessary methods
    mockClient = {
      config: {
        siteUrl: "https://test.wordpress.com",
      },
      getSiteUrl: vi.fn().mockReturnValue("https://test.wordpress.com"),
      getComments: vi.fn(),
      getComment: vi.fn(),
      createComment: vi.fn(),
      updateComment: vi.fn(),
      deleteComment: vi.fn(),
    };

    // Create CommentTools instance
    commentTools = new CommentTools();
  });

  describe("getTools", () => {
    it("should return all comment tool definitions", () => {
      const tools = commentTools.getTools();

      expect(tools).toHaveLength(7);
      expect(tools.map((t) => t.name)).toEqual([
        "wp_list_comments",
        "wp_get_comment",
        "wp_create_comment",
        "wp_update_comment",
        "wp_delete_comment",
        "wp_approve_comment",
        "wp_spam_comment",
      ]);
    });

    it("should include handlers for each tool", () => {
      const tools = commentTools.getTools();

      tools.forEach((tool) => {
        expect(tool.handler).toBeDefined();
        expect(typeof tool.handler).toBe("function");
      });
    });

    it("should include proper tool metadata", () => {
      const tools = commentTools.getTools();
      const listCommentsTool = tools.find((t) => t.name === "wp_list_comments");

      expect(listCommentsTool.description).toContain("Lists comments from a WordPress site");
      expect(listCommentsTool.inputSchema).toBeDefined();
      expect(typeof listCommentsTool.inputSchema).toBe("object");
    });

    it("should include status enum for comment filters", () => {
      const tools = commentTools.getTools();
      const listCommentsTool = tools.find((t) => t.name === "wp_list_comments");
      const statusProp = listCommentsTool.inputSchema.properties.status;

      expect(statusProp.enum).toEqual(["hold", "approve", "spam", "trash"]);
    });

    it("should include status enum for comment updates", () => {
      const tools = commentTools.getTools();
      const updateCommentTool = tools.find((t) => t.name === "wp_update_comment");
      const statusProp = updateCommentTool.inputSchema.properties.status;

      expect(statusProp.enum).toEqual(["hold", "approve", "spam", "trash"]);
    });
  });

  describe("handleListComments", () => {
    beforeEach(() => {
      mockClient.getComments.mockResolvedValue([
        {
          id: 1,
          author_name: "John Doe",
          post: 10,
          status: "approved",
          date: "2024-01-01T00:00:00",
          content: {
            rendered:
              "This is a great post! I really enjoyed reading it and learned a lot from the insights shared. Thank you for taking the time to write this detailed explanation.",
          },
        },
        {
          id: 2,
          author_name: "Jane Smith",
          post: 15,
          status: "hold",
          date: "2024-01-02T00:00:00",
          content: {
            rendered: "Interesting perspective on this topic. I have some additional thoughts to share...",
          },
        },
        {
          id: 3,
          author_name: "Bob Wilson",
          post: 10,
          status: "spam",
          date: "2024-01-03T00:00:00",
          content: {
            rendered: "Check out this amazing offer! Buy now and save 50% on all products!",
          },
        },
      ]);
    });

    it("should list comments with default parameters", async () => {
      const result = await commentTools.handleListComments(mockClient, {});

      expect(mockClient.getComments).toHaveBeenCalledWith({});
      expect(typeof result).toBe("string");
      expect(result).toContain("Found 3 comments");
      expect(result).toContain("John Doe");
      expect(result).toContain("Jane Smith");
      expect(result).toContain("Bob Wilson");
    });

    it("should handle post filter parameter", async () => {
      await commentTools.handleListComments(mockClient, { post: 10 });

      expect(mockClient.getComments).toHaveBeenCalledWith({ post: 10 });
    });

    it("should handle status filter parameter", async () => {
      await commentTools.handleListComments(mockClient, { status: "approved" });

      expect(mockClient.getComments).toHaveBeenCalledWith({ status: "approved" });
    });

    it("should handle combined post and status filters", async () => {
      await commentTools.handleListComments(mockClient, { post: 10, status: "hold" });

      expect(mockClient.getComments).toHaveBeenCalledWith({ post: 10, status: "hold" });
    });

    it("should format comment details correctly", async () => {
      const result = await commentTools.handleListComments(mockClient, {});

      expect(typeof result).toBe("string");
      expect(result).toContain("ID 1: By **John Doe** on Post 10 (approved)");
      expect(result).toContain(
        "This is a great post! I really enjoyed reading it and learned a lot from the insights shared. Thank ...",
      );
      expect(result).toContain("ID 2: By **Jane Smith** on Post 15 (hold)");
      expect(result).toContain("Interesting perspective on this topic. I have some additional thoughts to share......");
    });

    it("should truncate long comment content", async () => {
      const result = await commentTools.handleListComments(mockClient, {});

      expect(typeof result).toBe("string");
      // Should truncate at 100 characters and add "..."
      expect(result).toContain(
        "This is a great post! I really enjoyed reading it and learned a lot from the insights shared. Thank ...",
      );
      expect(result).not.toContain("Thank you for taking the time");
    });

    it("should handle empty results", async () => {
      mockClient.getComments.mockResolvedValue([]);

      const result = await commentTools.handleListComments(mockClient, {});

      expect(typeof result).toBe("string");
      expect(result).toContain("No comments found matching the criteria");
    });

    it("should handle API errors gracefully", async () => {
      mockClient.getComments.mockRejectedValue(new Error("API Error"));

      await expect(commentTools.handleListComments(mockClient, {})).rejects.toThrow(
        "Failed to list comments: API Error",
      );
    });
  });

  describe("handleGetComment", () => {
    beforeEach(() => {
      mockClient.getComment.mockResolvedValue({
        id: 1,
        author_name: "John Doe",
        post: 10,
        date: "2024-01-01T12:30:00",
        status: "approved",
        content: {
          rendered: "This is a detailed comment with full content that should be displayed completely.",
        },
      });
    });

    it("should get a comment by ID", async () => {
      const result = await commentTools.handleGetComment(mockClient, { id: 1 });

      expect(mockClient.getComment).toHaveBeenCalledWith(1);
      expect(typeof result).toBe("string");
      expect(result).toContain("Comment Details (ID: 1)");
      expect(result).toContain("John Doe");
    });

    it("should format comment details correctly", async () => {
      const result = await commentTools.handleGetComment(mockClient, { id: 1 });

      expect(typeof result).toBe("string");
      expect(result).toContain("**Author:** John Doe");
      expect(result).toContain("**Post ID:** 10");
      expect(result).toContain("**Date:**");
      expect(result).toContain("**Status:** approved");
      expect(result).toContain("**Content:** This is a detailed comment with full content");
    });

    it("should handle missing ID parameter", async () => {
      await expect(commentTools.handleGetComment(mockClient, {})).rejects.toThrow();
    });

    it("should handle non-existent comment", async () => {
      mockClient.getComment.mockRejectedValue(new Error("Comment not found"));

      await expect(commentTools.handleGetComment(mockClient, { id: 999 })).rejects.toThrow(
        "Failed to get comment: Comment not found",
      );
    });

    it("should format date correctly", async () => {
      const result = await commentTools.handleGetComment(mockClient, { id: 1 });

      expect(typeof result).toBe("string");
      expect(result).toContain("**Date:**");
      // Should contain formatted date/time (flexible for different locales)
      expect(result).toMatch(/\d{1,2}[./-]\d{1,2}[./-]\d{4}/); // Date format pattern (flexible)
    });
  });

  describe("handleCreateComment", () => {
    beforeEach(() => {
      mockClient.createComment.mockResolvedValue({
        id: 100,
        author_name: "New Commenter",
        post: 5,
        status: "hold",
        content: {
          rendered: "This is a new comment",
        },
      });
    });

    it("should create a comment with required parameters", async () => {
      const commentData = {
        post: 5,
        content: "This is a new comment",
      };

      const result = await commentTools.handleCreateComment(mockClient, commentData);

      expect(mockClient.createComment).toHaveBeenCalledWith(commentData);
      expect(typeof result).toBe("string");
      expect(result).toContain("Comment created successfully with ID: 100");
    });

    it("should create a comment with full parameters", async () => {
      const commentData = {
        post: 5,
        content: "This is a new comment",
        author_name: "Test Author",
        author_email: "test@example.com",
      };

      const result = await commentTools.handleCreateComment(mockClient, commentData);

      expect(mockClient.createComment).toHaveBeenCalledWith(commentData);
      expect(typeof result).toBe("string");
      expect(result).toContain("Comment created successfully with ID: 100");
    });

    it("should handle creation errors", async () => {
      mockClient.createComment.mockRejectedValue(new Error("Post not found"));

      await expect(
        commentTools.handleCreateComment(mockClient, {
          post: 999,
          content: "Test comment",
        }),
      ).rejects.toThrow("Failed to create comment: Post not found");
    });

    it("should handle invalid post ID", async () => {
      mockClient.createComment.mockRejectedValue(new Error("Invalid post ID"));

      await expect(
        commentTools.handleCreateComment(mockClient, {
          post: -1,
          content: "Test comment",
        }),
      ).rejects.toThrow("Failed to create comment: Invalid post ID");
    });

    it("should handle spam protection errors", async () => {
      mockClient.createComment.mockRejectedValue(new Error("Comment appears to be spam"));

      await expect(
        commentTools.handleCreateComment(mockClient, {
          post: 5,
          content: "BUY NOW! AMAZING OFFER!",
        }),
      ).rejects.toThrow("Failed to create comment: Comment appears to be spam");
    });
  });

  describe("handleUpdateComment", () => {
    beforeEach(() => {
      mockClient.updateComment.mockResolvedValue({
        id: 1,
        author_name: "John Doe",
        status: "approved",
        content: {
          rendered: "Updated comment content",
        },
      });
    });

    it("should update comment content", async () => {
      const result = await commentTools.handleUpdateComment(mockClient, {
        id: 1,
        content: "Updated comment content",
      });

      expect(mockClient.updateComment).toHaveBeenCalledWith({
        id: 1,
        content: "Updated comment content",
      });
      expect(typeof result).toBe("string");
      expect(result).toContain("Comment 1 updated successfully. New status: approved");
    });

    it("should update comment status", async () => {
      await commentTools.handleUpdateComment(mockClient, {
        id: 1,
        status: "approved",
      });

      expect(mockClient.updateComment).toHaveBeenCalledWith({
        id: 1,
        status: "approved",
      });
    });

    it("should update multiple fields", async () => {
      await commentTools.handleUpdateComment(mockClient, {
        id: 1,
        content: "Updated content",
        status: "approved",
      });

      expect(mockClient.updateComment).toHaveBeenCalledWith({
        id: 1,
        content: "Updated content",
        status: "approved",
      });
    });

    it("should handle missing ID", async () => {
      await expect(
        commentTools.handleUpdateComment(mockClient, {
          content: "Updated content",
        }),
      ).resolves.toBeDefined();
    });

    it("should handle update errors", async () => {
      mockClient.updateComment.mockRejectedValue(new Error("Comment not found"));

      await expect(
        commentTools.handleUpdateComment(mockClient, {
          id: 999,
          content: "Updated content",
        }),
      ).rejects.toThrow("Failed to update comment: Comment not found");
    });

    it("should handle permission errors", async () => {
      mockClient.updateComment.mockRejectedValue(new Error("Permission denied"));

      await expect(
        commentTools.handleUpdateComment(mockClient, {
          id: 1,
          status: "approved",
        }),
      ).rejects.toThrow("Failed to update comment: Permission denied");
    });
  });

  describe("handleDeleteComment", () => {
    beforeEach(() => {
      mockClient.deleteComment.mockResolvedValue({
        deleted: true,
        previous: {
          id: 1,
          status: "trash",
        },
      });
    });

    it("should delete comment to trash by default", async () => {
      const result = await commentTools.handleDeleteComment(mockClient, {
        id: 1,
      });

      expect(mockClient.deleteComment).toHaveBeenCalledWith(1, undefined);
      expect(typeof result).toBe("string");
      expect(result).toContain("Comment 1 has been moved to trash");
    });

    it("should permanently delete with force=true", async () => {
      const result = await commentTools.handleDeleteComment(mockClient, {
        id: 1,
        force: true,
      });

      expect(mockClient.deleteComment).toHaveBeenCalledWith(1, true);
      expect(typeof result).toBe("string");
      expect(result).toContain("Comment 1 has been permanently deleted");
    });

    it("should handle force=false explicitly", async () => {
      const result = await commentTools.handleDeleteComment(mockClient, {
        id: 1,
        force: false,
      });

      expect(mockClient.deleteComment).toHaveBeenCalledWith(1, false);
      expect(typeof result).toBe("string");
      expect(result).toContain("Comment 1 has been moved to trash");
    });

    it("should handle missing ID", async () => {
      await expect(commentTools.handleDeleteComment(mockClient, {})).rejects.toThrow();
    });

    it("should handle deletion errors", async () => {
      mockClient.deleteComment.mockRejectedValue(new Error("Comment not found"));

      await expect(
        commentTools.handleDeleteComment(mockClient, {
          id: 999,
        }),
      ).rejects.toThrow("Failed to delete comment: Comment not found");
    });

    it("should handle permission errors", async () => {
      mockClient.deleteComment.mockRejectedValue(new Error("Permission denied"));

      await expect(
        commentTools.handleDeleteComment(mockClient, {
          id: 1,
        }),
      ).rejects.toThrow("Failed to delete comment: Permission denied");
    });
  });

  describe("handleApproveComment", () => {
    beforeEach(() => {
      mockClient.updateComment.mockResolvedValue({
        id: 1,
        author_name: "John Doe",
        status: "approved",
        content: {
          rendered: "Approved comment",
        },
      });
    });

    it("should approve a comment", async () => {
      const result = await commentTools.handleApproveComment(mockClient, {
        id: 1,
      });

      expect(mockClient.updateComment).toHaveBeenCalledWith({
        id: 1,
        status: "approved",
      });
      expect(typeof result).toBe("string");
      expect(result).toContain("Comment 1 has been approved");
    });

    it("should handle missing ID", async () => {
      await expect(commentTools.handleApproveComment(mockClient, {})).rejects.toThrow();
    });

    it("should handle approval errors", async () => {
      mockClient.updateComment.mockRejectedValue(new Error("Comment not found"));

      await expect(
        commentTools.handleApproveComment(mockClient, {
          id: 999,
        }),
      ).rejects.toThrow("Failed to approve comment: Comment not found");
    });

    it("should handle permission errors", async () => {
      mockClient.updateComment.mockRejectedValue(new Error("Permission denied"));

      await expect(
        commentTools.handleApproveComment(mockClient, {
          id: 1,
        }),
      ).rejects.toThrow("Failed to approve comment: Permission denied");
    });

    it("should handle already approved comments", async () => {
      mockClient.updateComment.mockResolvedValue({
        id: 1,
        status: "approved",
      });

      const result = await commentTools.handleApproveComment(mockClient, { id: 1 });

      expect(typeof result).toBe("string");
      expect(result).toContain("Comment 1 has been approved");
    });
  });

  describe("handleSpamComment", () => {
    beforeEach(() => {
      mockClient.updateComment.mockResolvedValue({
        id: 1,
        author_name: "Spammer",
        status: "spam",
        content: {
          rendered: "Spam comment content",
        },
      });
    });

    it("should mark a comment as spam", async () => {
      const result = await commentTools.handleSpamComment(mockClient, {
        id: 1,
      });

      expect(mockClient.updateComment).toHaveBeenCalledWith({
        id: 1,
        status: "spam",
      });
      expect(typeof result).toBe("string");
      expect(result).toContain("Comment 1 has been marked as spam");
    });

    it("should handle missing ID", async () => {
      await expect(commentTools.handleSpamComment(mockClient, {})).rejects.toThrow();
    });

    it("should handle spam marking errors", async () => {
      mockClient.updateComment.mockRejectedValue(new Error("Comment not found"));

      await expect(
        commentTools.handleSpamComment(mockClient, {
          id: 999,
        }),
      ).rejects.toThrow("Failed to mark comment as spam: Comment not found");
    });

    it("should handle permission errors", async () => {
      mockClient.updateComment.mockRejectedValue(new Error("Permission denied"));

      await expect(
        commentTools.handleSpamComment(mockClient, {
          id: 1,
        }),
      ).rejects.toThrow("Failed to mark comment as spam: Permission denied");
    });

    it("should handle already spam comments", async () => {
      mockClient.updateComment.mockResolvedValue({
        id: 1,
        status: "spam",
      });

      const result = await commentTools.handleSpamComment(mockClient, { id: 1 });

      expect(typeof result).toBe("string");
      expect(result).toContain("Comment 1 has been marked as spam");
    });
  });

  describe("edge cases and error handling", () => {
    it("should handle network errors", async () => {
      mockClient.getComment.mockRejectedValue(new Error("ECONNREFUSED"));

      await expect(commentTools.handleGetComment(mockClient, { id: 1 })).rejects.toThrow(
        "Failed to get comment: ECONNREFUSED",
      );
    });

    it("should handle malformed responses", async () => {
      mockClient.getComment.mockResolvedValue(null);

      await expect(commentTools.handleGetComment(mockClient, { id: 1 })).rejects.toThrow();
    });

    it("should handle authentication errors", async () => {
      mockClient.createComment.mockRejectedValue(new Error("401 Unauthorized"));

      await expect(
        commentTools.handleCreateComment(mockClient, {
          post: 1,
          content: "Test comment",
        }),
      ).rejects.toThrow("Failed to create comment: 401 Unauthorized");
    });

    it("should handle rate limiting", async () => {
      mockClient.getComments.mockRejectedValue(new Error("429 Too Many Requests"));

      await expect(commentTools.handleListComments(mockClient, {})).rejects.toThrow(
        "Failed to list comments: 429 Too Many Requests",
      );
    });

    it("should handle invalid comment IDs", async () => {
      await expect(commentTools.handleGetComment(mockClient, { id: -1 })).rejects.toThrow();
    });

    it("should handle server errors", async () => {
      mockClient.updateComment.mockRejectedValue(new Error("500 Internal Server Error"));

      await expect(
        commentTools.handleUpdateComment(mockClient, {
          id: 1,
          content: "Test",
        }),
      ).rejects.toThrow("Failed to update comment: 500 Internal Server Error");
    });

    it("should handle comments with short content", async () => {
      mockClient.getComments.mockResolvedValue([
        {
          id: 1,
          author_name: "Short Commenter",
          post: 1,
          status: "approved",
          content: {
            rendered: "Short",
          },
        },
      ]);

      const result = await commentTools.handleListComments(mockClient, {});

      expect(typeof result).toBe("string");
      expect(result).toContain("Short...");
    });

    it("should handle comments with exactly 100 characters", async () => {
      const exactContent = "A".repeat(100);
      mockClient.getComments.mockResolvedValue([
        {
          id: 1,
          author_name: "Exact Commenter",
          post: 1,
          status: "approved",
          content: {
            rendered: exactContent,
          },
        },
      ]);

      const result = await commentTools.handleListComments(mockClient, {});

      expect(typeof result).toBe("string");
      expect(result).toContain(exactContent + "...");
    });

    it("should handle empty comment content", async () => {
      mockClient.getComments.mockResolvedValue([
        {
          id: 1,
          author_name: "Empty Commenter",
          post: 1,
          status: "approved",
          content: {
            rendered: "",
          },
        },
      ]);

      const result = await commentTools.handleListComments(mockClient, {});

      expect(typeof result).toBe("string");
      expect(result).toContain("...");
    });
  });
});
