/**
 * Tests for POST /api/dashboard/reviews/requests/send
 * Covers: auth, validation, email delivery, WhatsApp skip, widget settings lookup, error handling
 */

jest.mock("@/lib/auth", () => ({ auth: jest.fn() }));
jest.mock("@/lib/utils/auth-helpers.util", () => ({
  getAuthenticatedUser: jest.fn(),
}));
jest.mock("@/lib/utils/organization-data", () => ({
  getEffectiveOwnerId: jest.fn((id: string) => id),
}));
jest.mock("@/lib/services/reviews.service", () => ({
  sendReviewRequest: jest.fn(),
}));
jest.mock("@/lib/queue/email-queue", () => ({
  queueEmail: jest.fn(),
}));
jest.mock("@/lib/email/review-request-email", () => ({
  buildReviewRequestEmailHtml: jest.fn().mockReturnValue({
    subject: "We'd love your feedback!",
    html: "<html>test</html>",
    text: "test",
  }),
}));
jest.mock("@/lib/db");
jest.mock("@/lib/logger", () => ({
  logger: {
    scope: () => ({
      info: jest.fn(),
      error: jest.fn(),
      warn: jest.fn(),
      debug: jest.fn(),
    }),
  },
}));

import { NextRequest } from "next/server";
import { getAuthenticatedUser } from "@/lib/utils/auth-helpers.util";
import * as reviewsService from "@/lib/services/reviews.service";
import { queueEmail } from "@/lib/queue/email-queue";
import { buildReviewRequestEmailHtml } from "@/lib/email/review-request-email";
import { prisma } from "@/lib/db";

const mockUser = { userId: "user-1", email: "admin@test.com" };

function makeReq(body?: object) {
  const opts: RequestInit = { method: "POST" };
  if (body) {
    opts.body = JSON.stringify(body);
    opts.headers = { "Content-Type": "application/json" };
  }
  return new NextRequest(
    "http://localhost:9000/api/dashboard/reviews/requests/send",
    opts,
  );
}

const baseEmailResult = {
  id: "req-1",
  customerName: "Alice",
  customerEmail: "alice@test.com",
  customerPhone: null,
  channel: "email",
  language: "en",
  token: "abc123token",
};

describe("POST /api/dashboard/reviews/requests/send", () => {
  let mod: typeof import("@/app/api/dashboard/reviews/requests/send/route");

  beforeAll(async () => {
    mod = await import("@/app/api/dashboard/reviews/requests/send/route");
  });

  beforeEach(() => {
    jest.clearAllMocks();
    (prisma.review_widget_settings.findUnique as jest.Mock).mockResolvedValue(
      null,
    );
  });

  // ── Auth ───────────────────────────────────────────────────────────────────

  it("returns 401 when unauthenticated", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(null);
    const res = await mod.POST(makeReq({ requestId: "req-1" }));
    expect(res.status).toBe(401);
    const json = await res.json();
    expect(json.success).toBe(false);
    expect(json.error).toBe("Unauthorized");
  });

  // ── Validation ─────────────────────────────────────────────────────────────

  it("returns 400 when requestId is missing", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    const res = await mod.POST(makeReq({}));
    expect(res.status).toBe(400);
    const json = await res.json();
    expect(json.error).toBe("requestId is required");
  });

  it("returns 400 when requestId is not a string", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    const res = await mod.POST(makeReq({ requestId: 42 }));
    expect(res.status).toBe(400);
    const json = await res.json();
    expect(json.error).toBe("requestId is required");
  });

  it("returns 404 when review request not found", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (reviewsService.sendReviewRequest as jest.Mock).mockResolvedValue(null);
    const res = await mod.POST(makeReq({ requestId: "nonexistent" }));
    expect(res.status).toBe(404);
    const json = await res.json();
    expect(json.error).toBe("Review request not found");
  });

  // ── Email delivery ─────────────────────────────────────────────────────────

  it("queues email for email channel with email address", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (reviewsService.sendReviewRequest as jest.Mock).mockResolvedValue(
      baseEmailResult,
    );
    (queueEmail as jest.Mock).mockResolvedValue(undefined);

    const res = await mod.POST(makeReq({ requestId: "req-1" }));
    expect(res.status).toBe(200);
    const json = await res.json();
    expect(json.success).toBe(true);
    expect(json.emailQueued).toBe(true);
    expect(queueEmail).toHaveBeenCalledTimes(1);
    expect(queueEmail).toHaveBeenCalledWith(
      expect.objectContaining({
        to: "alice@test.com",
        category: "review_request",
        metadata: { reviewRequestId: "req-1" },
      }),
    );
  });

  it("builds review URL with correct token", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (reviewsService.sendReviewRequest as jest.Mock).mockResolvedValue(
      baseEmailResult,
    );
    (queueEmail as jest.Mock).mockResolvedValue(undefined);

    await mod.POST(makeReq({ requestId: "req-1" }));

    expect(buildReviewRequestEmailHtml).toHaveBeenCalledWith(
      expect.objectContaining({
        reviewUrl: expect.stringContaining("abc123token"),
        customerName: "Alice",
        isReminder: false,
      }),
    );
  });

  it("uses businessName from widget settings when available", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (reviewsService.sendReviewRequest as jest.Mock).mockResolvedValue(
      baseEmailResult,
    );
    (prisma.review_widget_settings.findUnique as jest.Mock).mockResolvedValue({
      businessName: "Test Clinic",
      businessNameAr: "عيادة اختبار",
    });
    (queueEmail as jest.Mock).mockResolvedValue(undefined);

    await mod.POST(makeReq({ requestId: "req-1" }));

    expect(buildReviewRequestEmailHtml).toHaveBeenCalledWith(
      expect.objectContaining({ businessName: "Test Clinic" }),
    );
  });

  it("falls back to 'Mawidi' when widget settings not found", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (reviewsService.sendReviewRequest as jest.Mock).mockResolvedValue(
      baseEmailResult,
    );
    (prisma.review_widget_settings.findUnique as jest.Mock).mockResolvedValue(
      null,
    );
    (queueEmail as jest.Mock).mockResolvedValue(undefined);

    await mod.POST(makeReq({ requestId: "req-1" }));

    expect(buildReviewRequestEmailHtml).toHaveBeenCalledWith(
      expect.objectContaining({ businessName: "Mawidi" }),
    );
  });

  it("uses businessNameAr for Arabic language requests", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (reviewsService.sendReviewRequest as jest.Mock).mockResolvedValue({
      ...baseEmailResult,
      language: "ar",
    });
    (prisma.review_widget_settings.findUnique as jest.Mock).mockResolvedValue({
      businessName: "Test Clinic",
      businessNameAr: "عيادة اختبار",
    });
    (queueEmail as jest.Mock).mockResolvedValue(undefined);

    await mod.POST(makeReq({ requestId: "req-1" }));

    expect(buildReviewRequestEmailHtml).toHaveBeenCalledWith(
      expect.objectContaining({ businessName: "عيادة اختبار", language: "ar" }),
    );
  });

  // ── Channel conditions ─────────────────────────────────────────────────────

  it("does NOT queue email for WhatsApp channel even when email exists", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (reviewsService.sendReviewRequest as jest.Mock).mockResolvedValue({
      ...baseEmailResult,
      channel: "whatsapp",
    });

    const res = await mod.POST(makeReq({ requestId: "req-1" }));
    expect(res.status).toBe(200);
    const json = await res.json();
    expect(json.emailQueued).toBe(false);
    expect(queueEmail).not.toHaveBeenCalled();
  });

  it("does NOT queue email when customer has no email address", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (reviewsService.sendReviewRequest as jest.Mock).mockResolvedValue({
      ...baseEmailResult,
      customerEmail: null,
    });

    const res = await mod.POST(makeReq({ requestId: "req-1" }));
    expect(res.status).toBe(200);
    const json = await res.json();
    expect(json.emailQueued).toBe(false);
    expect(queueEmail).not.toHaveBeenCalled();
  });

  // ── Resilience ─────────────────────────────────────────────────────────────

  it("still returns success when email queue fails", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (reviewsService.sendReviewRequest as jest.Mock).mockResolvedValue(
      baseEmailResult,
    );
    (queueEmail as jest.Mock).mockRejectedValue(new Error("SMTP timeout"));

    const res = await mod.POST(makeReq({ requestId: "req-1" }));
    expect(res.status).toBe(200);
    const json = await res.json();
    expect(json.success).toBe(true);
    // Email failed silently — emailQueued should be false
    expect(json.emailQueued).toBe(false);
  });

  it("still returns success when widget settings DB call fails", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (reviewsService.sendReviewRequest as jest.Mock).mockResolvedValue(
      baseEmailResult,
    );
    (prisma.review_widget_settings.findUnique as jest.Mock).mockRejectedValue(
      new Error("DB error"),
    );
    (queueEmail as jest.Mock).mockResolvedValue(undefined);

    const res = await mod.POST(makeReq({ requestId: "req-1" }));
    expect(res.status).toBe(200);
    const json = await res.json();
    expect(json.success).toBe(true);
    expect(json.emailQueued).toBe(true);
    // Falls back to "Mawidi" when widget settings fail
    expect(buildReviewRequestEmailHtml).toHaveBeenCalledWith(
      expect.objectContaining({ businessName: "Mawidi" }),
    );
  });

  it("returns 500 on unexpected service error", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (reviewsService.sendReviewRequest as jest.Mock).mockRejectedValue(
      new Error("DB connection lost"),
    );

    const res = await mod.POST(makeReq({ requestId: "req-1" }));
    expect(res.status).toBe(500);
    const json = await res.json();
    expect(json.success).toBe(false);
    expect(json.error).toBe("Internal server error");
  });

  // ── Response shape ─────────────────────────────────────────────────────────

  it("includes reviewRequest in response body", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(mockUser);
    (reviewsService.sendReviewRequest as jest.Mock).mockResolvedValue(
      baseEmailResult,
    );
    (queueEmail as jest.Mock).mockResolvedValue(undefined);

    const res = await mod.POST(makeReq({ requestId: "req-1" }));
    const json = await res.json();
    expect(json.reviewRequest).toEqual(baseEmailResult);
  });
});
