/**
 * Tests for Reviews Service (Convex-backed) — featured/embed/questionResponses
 */

jest.mock("@/lib/convex-client", () => ({
  __esModule: true,
  convexQuery: jest.fn().mockResolvedValue(null),
  convexMutation: jest.fn().mockResolvedValue(null),
  convexAction: jest.fn().mockResolvedValue(null),
}));

jest.mock("@/lib/logger", () => ({
  logger: {
    info: jest.fn(),
    warn: jest.fn(),
    error: jest.fn(),
    debug: jest.fn(),
    scope: jest.fn().mockReturnValue({
      info: jest.fn(),
      warn: jest.fn(),
      error: jest.fn(),
      debug: jest.fn(),
    }),
  },
}));

import { convexQuery, convexMutation } from "@/lib/convex-client";
import {
  toggleFeaturedReview,
  listFeaturedReviews,
  reorderFeaturedReviews,
  getPublicFeaturedReviews,
  generateEmbedCode,
  submitReviewViaToken,
} from "@/lib/services/reviews.service";

const mockConvexQuery = convexQuery as jest.Mock;
const mockConvexMutation = convexMutation as jest.Mock;

describe("toggleFeaturedReview", () => {
  beforeEach(() => jest.clearAllMocks());

  it("features a review via Convex mutation", async () => {
    mockConvexMutation.mockResolvedValueOnce("rev-1");

    const result = await toggleFeaturedReview("user-1", "rev-1", true);
    expect(result).not.toBeNull();
    expect(result!.id).toBe("rev-1");
    expect(mockConvexMutation).toHaveBeenCalledWith(
      "reviews/mutations:updateReview",
      expect.objectContaining({
        userId: "user-1",
        reviewId: "rev-1",
        isFeatured: true,
      }),
    );
  });

  it("unfeatures a review", async () => {
    mockConvexMutation.mockResolvedValueOnce("rev-1");

    const result = await toggleFeaturedReview("user-1", "rev-1", false);
    expect(result).not.toBeNull();
    expect(mockConvexMutation).toHaveBeenCalledWith(
      "reviews/mutations:updateReview",
      expect.objectContaining({
        isFeatured: false,
      }),
    );
  });

  it("returns null when mutation returns null (not found)", async () => {
    mockConvexMutation.mockResolvedValueOnce(null);

    const result = await toggleFeaturedReview("user-1", "rev-other", true);
    expect(result).toBeNull();
  });
});

describe("listFeaturedReviews", () => {
  beforeEach(() => jest.clearAllMocks());

  it("returns featured reviews from Convex", async () => {
    const mockReviews = [
      {
        _id: "rev-1",
        userId: "user-1",
        customerName: "Alice",
        rating: 5,
        isFeatured: true,
        featuredOrder: 1,
        createdAt: Date.now(),
        updatedAt: Date.now(),
      },
      {
        _id: "rev-2",
        userId: "user-1",
        customerName: "Bob",
        rating: 4,
        isFeatured: true,
        featuredOrder: 2,
        createdAt: Date.now(),
        updatedAt: Date.now(),
      },
    ];
    mockConvexQuery.mockResolvedValueOnce(mockReviews);

    const result = await listFeaturedReviews("user-1");
    expect(result).toHaveLength(2);
    expect(mockConvexQuery).toHaveBeenCalledWith(
      "reviews/queries:getFeaturedReviews",
      { userId: "user-1" },
    );
  });

  it("returns empty array when no featured reviews", async () => {
    mockConvexQuery.mockResolvedValueOnce(null);

    const result = await listFeaturedReviews("user-1");
    expect(result).toEqual([]);
  });
});

describe("reorderFeaturedReviews", () => {
  beforeEach(() => jest.clearAllMocks());

  it("calls Convex mutation with ordered IDs", async () => {
    mockConvexMutation.mockResolvedValueOnce(null);

    await reorderFeaturedReviews("user-1", ["rev-b", "rev-a", "rev-c"]);

    expect(mockConvexMutation).toHaveBeenCalledWith(
      "reviews/mutations:reorderFeaturedReviews",
      {
        userId: "user-1",
        orderedIds: ["rev-b", "rev-a", "rev-c"],
      },
    );
  });
});

describe("getPublicFeaturedReviews", () => {
  beforeEach(() => jest.clearAllMocks());

  it("returns public-safe fields only", async () => {
    const mockReviews = [
      {
        _id: "rev-1",
        userId: "user-1",
        customerName: "John",
        customerEmail: "john@example.com",
        rating: 5,
        title: "Great!",
        content: "Loved it",
        language: "en",
        platform: "website",
        isFeatured: true,
        createdAt: Date.now(),
        updatedAt: Date.now(),
      },
    ];
    mockConvexQuery.mockResolvedValueOnce(mockReviews);

    const result = await getPublicFeaturedReviews("user-1");
    expect(result).toHaveLength(1);
    expect(result[0]).toHaveProperty("id");
    expect(result[0]).toHaveProperty("customerName");
    expect(result[0]).not.toHaveProperty("customerEmail");
    expect(result[0]).not.toHaveProperty("userId");
  });
});

describe("generateEmbedCode", () => {
  it("returns valid iframe HTML with correct URL", () => {
    const result = generateEmbedCode("user-123", "https://mawidi.com");
    expect(result).toContain("<iframe");
    expect(result).toContain("user-123");
    expect(result).toContain("https://mawidi.com");
    expect(result).toContain('width="100%"');
  });

  it("encodes user ID in URL", () => {
    const result = generateEmbedCode("user with spaces", "https://mawidi.com");
    expect(result).toContain("user%20with%20spaces");
  });
});

describe("submitReviewViaToken with questionResponses", () => {
  beforeEach(() => jest.clearAllMocks());

  it("saves questionResponses to review", async () => {
    const mockRequest = {
      _id: "req-1",
      userId: "user-1",
      customerName: "Customer",
      customerEmail: "test@example.com",
      customerPhone: "+97412345678",
      channel: "email",
      token: "token-abc",
      language: "en",
      status: "PENDING",
      expiresAt: Date.now() + 86400000,
      source: "manual",
      reminderCount: 0,
      createdAt: Date.now(),
      updatedAt: Date.now(),
    };
    // First: convexQuery for request lookup
    mockConvexQuery.mockResolvedValueOnce(mockRequest);
    // Second: convexMutation to create review
    mockConvexMutation.mockResolvedValueOnce("mock-review-id");

    const input = {
      rating: 5,
      customerName: "John",
      language: "en" as const,
      questionResponses: [{ questionEn: "How was it?", answer: "Great!" }],
    };

    const result = await submitReviewViaToken("token-abc", input);
    expect(result).not.toBeNull();
    expect(result!.id).toBe("mock-review-id");
    expect(mockConvexMutation).toHaveBeenCalledWith(
      "reviews/mutations:createReview",
      expect.objectContaining({
        rating: 5,
        questionResponses: [{ questionEn: "How was it?", answer: "Great!" }],
      }),
    );
  });

  it("handles empty questionResponses", async () => {
    const mockRequest = {
      _id: "req-1",
      userId: "user-1",
      customerName: "Customer",
      customerEmail: null,
      customerPhone: null,
      channel: "sms",
      token: "token-xyz",
      language: "ar",
      status: "SENT",
      expiresAt: Date.now() + 86400000,
      source: "manual",
      reminderCount: 0,
      createdAt: Date.now(),
      updatedAt: Date.now(),
    };
    mockConvexQuery.mockResolvedValueOnce(mockRequest);
    mockConvexMutation.mockResolvedValueOnce("mock-review-id");

    const input = {
      rating: 4,
      customerName: "Jane",
      language: "ar" as const,
    };

    const result = await submitReviewViaToken("token-xyz", input);
    expect(result).not.toBeNull();
  });

  it("returns null for invalid/expired token", async () => {
    mockConvexQuery.mockResolvedValueOnce(null);

    const result = await submitReviewViaToken("invalid-token", {
      rating: 5,
      customerName: "John",
      language: "en",
    });
    expect(result).toBeNull();
  });
});
