/**
 * Unit Tests: Passwordless Authentication APIs
 *
 * Tests the API endpoints for passwordless signup and login
 */

import { NextRequest } from "next/server";

// Mock dependencies
jest.mock("@/lib/db");

jest.mock("@/lib/otp-storage", () => ({
  generateOTP: jest.fn(() => "123456"),
  sendOTP: jest.fn(() => Promise.resolve(true)),
  verifyOTP: jest.fn(() => Promise.resolve(true)),
  getRemainingAttempts: jest.fn(() => Promise.resolve(5)),
}));

jest.mock("@/lib/rate-limiter", () => ({
  withRateLimit: (handler: any) => handler,
  rateLimiters: {
    otpSend: {},
    otpVerify: {},
    login: {},
  },
  getEmailIdentifier: jest.fn(),
}));

jest.mock("@/lib/csrf", () => ({
  withCSRF: (handler: any) => handler,
}));

jest.mock("@/lib/input-validation", () => ({
  validateEmail: jest.fn((email: string) => ({
    valid: email.includes("@"),
    error: email.includes("@") ? null : "Invalid email",
  })),
  validateOTPCode: jest.fn((code: string) => ({
    valid: /^\d{6}$/.test(code),
    error: /^\d{6}$/.test(code) ? null : "Invalid OTP format",
  })),
  sanitizeInput: jest.fn((input: string) => input),
}));

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

jest.mock("@/lib/dual-database", () => ({
  saveToBothDatabases: jest.fn(),
  saveUserToBoth: jest.fn(),
  saveAuditLogToBoth: jest.fn(),
}));

// Auth routes restructured
describe.skip("GET /api/auth/check-email", () => {
  let checkEmailHandler: any;

  beforeEach(async () => {
    jest.clearAllMocks();

    // Import fresh module
    const module = await import("@/app/api/auth/check-email/route");
    checkEmailHandler = module.GET;
  });

  test("should return exists: false for new email", async () => {
    const { prisma } = require("@/lib/db");
    prisma.users.findUnique.mockResolvedValue(null);

    const request = new Request(
      "http://localhost:9000/api/auth/check-email?email=new@example.com",
    );
    const response = await checkEmailHandler(request);
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(data.exists).toBe(false);
    expect(data.available).toBe(true);
  });

  test("should return exists: true for existing email", async () => {
    const { prisma } = require("@/lib/db");
    prisma.users.findUnique.mockResolvedValue({
      id: "user123",
      email: "existing@example.com",
      emailVerified: new Date(),
      googleId: null,
    });

    const request = new Request(
      "http://localhost:9000/api/auth/check-email?email=existing@example.com",
    );
    const response = await checkEmailHandler(request);
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(data.exists).toBe(true);
    expect(data.verified).toBe(true);
    expect(data.isGoogleAccount).toBe(false);
  });

  test("should detect Google accounts", async () => {
    const { prisma } = require("@/lib/db");
    prisma.users.findUnique.mockResolvedValue({
      id: "user123",
      email: "google@example.com",
      emailVerified: new Date(),
      googleId: "google-id-123",
    });

    const request = new Request(
      "http://localhost:9000/api/auth/check-email?email=google@example.com",
    );
    const response = await checkEmailHandler(request);
    const data = await response.json();

    expect(data.exists).toBe(true);
    expect(data.isGoogleAccount).toBe(true);
  });

  test("should validate email format", async () => {
    const request = new Request(
      "http://localhost:9000/api/auth/check-email?email=invalid-email",
    );
    const response = await checkEmailHandler(request);
    const data = await response.json();

    expect(response.status).toBe(400);
    expect(data.error).toBeTruthy();
  });

  test("should require email parameter", async () => {
    const request = new Request("http://localhost:9000/api/auth/check-email");
    const response = await checkEmailHandler(request);
    const data = await response.json();

    expect(response.status).toBe(400);
    expect(data.error).toContain("required");
  });
});

describe.skip("POST /api/auth/verify-signup-otp", () => {
  test("should create passwordless account after OTP verification", async () => {
    const { prisma } = require("@/lib/db");
    const { verifyOTP } = require("@/lib/otp-storage");

    // Mock: User doesn't exist yet
    prisma.users.findUnique.mockResolvedValue(null);

    // Mock: OTP is valid
    verifyOTP.mockResolvedValue(true);

    // Mock: User creation
    const mockUser = {
      id: "new-user-123",
      email: "newuser@example.com",
      name: "New User",
      password: null, // Passwordless!
      emailVerified: new Date(),
      createdAt: new Date(),
      updatedAt: new Date(),
    };
    prisma.users.create.mockResolvedValue(mockUser);

    // Mock audit log
    prisma.audit_logs.create.mockResolvedValue({});

    const module = await import("@/app/api/auth/verify-signup-otp/route");
    const handler = module.POST;

    const request = new Request(
      "http://localhost:9000/api/auth/verify-signup-otp",
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          email: "newuser@example.com",
          code: "123456",
          fullName: "New User",
          selectedPackage: "tier2",
        }),
      },
    );

    const response = await handler(request as any);
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(data.success).toBe(true);
    expect(data.userId).toBe("new-user-123");

    // Verify passwordless account was created
    expect(prisma.users.create).toHaveBeenCalledWith(
      expect.objectContaining({
        data: expect.objectContaining({
          email: "newuser@example.com",
          name: "New User",
          password: null, // No password!
          emailVerified: expect.any(Date),
        }),
      }),
    );

    // Verify audit log mentions passwordless
    expect(prisma.audit_logs.create).toHaveBeenCalledWith(
      expect.objectContaining({
        data: expect.objectContaining({
          metadata: expect.objectContaining({
            passwordless: true,
          }),
        }),
      }),
    );
  });

  test("should reject invalid OTP", async () => {
    const { verifyOTP } = require("@/lib/otp-storage");
    verifyOTP.mockResolvedValue(false);

    const module = await import("@/app/api/auth/verify-signup-otp/route");
    const handler = module.POST;

    const request = new Request(
      "http://localhost:9000/api/auth/verify-signup-otp",
      {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          email: "test@example.com",
          code: "000000",
        }),
      },
    );

    const response = await handler(request as any);
    const data = await response.json();

    expect(response.status).toBe(401);
    expect(data.error).toMatch(/invalid.*expired/i);
  });
});

describe.skip("POST /api/auth/login (passwordless)", () => {
  test("should send personalized OTP for email login", async () => {
    const { prisma } = require("@/lib/db");
    const { sendOTP } = require("@/lib/otp-storage");

    // Mock: User exists
    prisma.users.findUnique.mockResolvedValue({
      id: "user123",
      email: "test@example.com",
      name: "Test User",
    });

    // Mock audit log
    prisma.audit_logs.create.mockResolvedValue({});

    const module = await import("@/app/api/auth/login/route");
    const handler = module.POST;

    const request = new Request("http://localhost:9000/api/auth/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        identifier: "test@example.com",
        useOTP: true,
        language: "en",
      }),
    });

    const response = await handler(request as any);
    const data = await response.json();

    expect(response.status).toBe(200);
    expect(data.success).toBe(true);
    expect(data.requiresOTP).toBe(true);

    // Verify sendOTP was called with user's name
    expect(sendOTP).toHaveBeenCalledWith(
      "test@example.com",
      expect.any(String), // OTP code
      "en",
      "Test User", // Name should be included!
      "login", // Purpose should be 'login'
    );
  });

  test("should default to OTP login when no password provided", async () => {
    const { prisma } = require("@/lib/db");
    const { sendOTP } = require("@/lib/otp-storage");

    prisma.users.findUnique.mockResolvedValue({
      id: "user123",
      email: "test@example.com",
      name: "Test User",
    });

    prisma.audit_logs.create.mockResolvedValue({});

    const module = await import("@/app/api/auth/login/route");
    const handler = module.POST;

    // Note: No password field, no useOTP flag - should default to OTP
    const request = new Request("http://localhost:9000/api/auth/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        identifier: "test@example.com",
        language: "en",
      }),
    });

    const response = await handler(request as any);
    const data = await response.json();

    expect(data.requiresOTP).toBe(true);
    expect(sendOTP).toHaveBeenCalled();
  });

  test("should return error for non-existent email", async () => {
    const { prisma } = require("@/lib/db");

    prisma.users.findUnique.mockResolvedValue(null);

    const module = await import("@/app/api/auth/login/route");
    const handler = module.POST;

    const request = new Request("http://localhost:9000/api/auth/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        identifier: "nonexistent@example.com",
        useOTP: true,
      }),
    });

    const response = await handler(request as any);
    const data = await response.json();

    expect(response.status).toBe(401);
    expect(data.error).toBeTruthy();
  });
});

describe.skip("Email Template Personalization", () => {
  test("createOTPEmail should include user name in greeting", () => {
    const { createOTPEmail } = require("@/lib/email");

    const template = createOTPEmail("123456", "en", "John Doe", "login");

    expect(template.html).toContain("Hello John Doe");
    expect(template.html).toContain("123456");
    expect(template.html).toContain("log in"); // Login-specific message
  });

  test("createOTPEmail should use generic greeting if no name", () => {
    const { createOTPEmail } = require("@/lib/email");

    const template = createOTPEmail("123456", "en", undefined, "login");

    expect(template.html).toContain("Hello,");
    expect(template.html).not.toContain("undefined");
  });

  test("createOTPEmail should have purpose-specific messages", () => {
    const { createOTPEmail } = require("@/lib/email");

    const loginEmail = createOTPEmail("123456", "en", "User", "login");
    expect(loginEmail.html).toMatch(/log.*in|login/i);

    const signupEmail = createOTPEmail("123456", "en", "User", "signup");
    expect(signupEmail.html).toMatch(/thank you.*choosing|registration/i);

    const resetEmail = createOTPEmail("123456", "en", "User", "reset");
    expect(resetEmail.html).toMatch(/reset.*password/i);
  });

  test("createOTPEmail should work in Arabic with name", () => {
    const { createOTPEmail } = require("@/lib/email");

    const template = createOTPEmail("123456", "ar", "أحمد", "login");

    expect(template.html).toContain("مرحباً أحمد");
    expect(template.html).toContain("123456");
    expect(template.subject).toMatch(/رمز|Mawidi/);
  });

  test("email should have security notice", () => {
    const { createOTPEmail } = require("@/lib/email");

    const template = createOTPEmail("123456", "en", "User", "login");

    expect(template.html).toMatch(/security notice|never share/i);
    expect(template.text).toMatch(/security notice|never share/i);
  });

  test("email should have support link", () => {
    const { createOTPEmail } = require("@/lib/email");

    const template = createOTPEmail("123456", "en", "User", "login");

    expect(template.html).toContain("support@mawidi.com");
    expect(template.html).toMatch(/contact support|need help/i);
  });
});
