/**
 * Unit tests for /api/register
 * Tests user registration API, validation, rate limiting, CSRF protection
 */

// Mock all dependencies BEFORE imports
jest.mock("@/lib/auth", () => ({
  registerUser: jest.fn(),
}));

jest.mock("@/lib/rate-limiter", () => ({
  withRateLimit: jest.fn((handler) => handler),
  rateLimiters: { register: {} },
}));

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

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

jest.mock("next/server", () => ({
  NextRequest: jest.fn(),
  NextResponse: {
    json: jest.fn((data, options) => ({
      status: options?.status || 200,
      json: async () => data,
    })),
  },
}));

import { NextRequest, NextResponse } from "next/server";
import { registerUser } from "@/lib/auth";
import { logger } from "@/lib/logger";

// Register route disabled - uses Clerk sign-up now
describe.skip("POST /api/register", () => {
  let registerModule: any;

  beforeAll(async () => {
    registerModule = await import("@/app/api/register/route");
  });

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

  const mockRequest = (body: any) =>
    ({
      json: jest.fn().mockResolvedValue(body),
      headers: {
        get: jest.fn(() => null),
      },
    }) as unknown as NextRequest;

  describe("Successful Registration", () => {
    it("should register user with valid data", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: true,
        userId: "user-123",
      });

      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
        fullName: "Test User",
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(201);
      expect(body.success).toBe(true);
      expect(body.userId).toBe("user-123");
      expect(body.message).toBe("User registered successfully");
    });

    it("should call registerUser with sanitized inputs", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: true,
        userId: "user-123",
      });

      const request = mockRequest({
        email: "TEST@EXAMPLE.COM", // Should be lowercased
        password: "SecurePass123!@#",
        fullName: "Test User",
      });

      await registerModule.POST(request);

      expect(registerUser).toHaveBeenCalledWith(
        "test@example.com", // Lowercased
        "SecurePass123!@#",
        "Test User",
        undefined, // No package selected
      );
    });

    it("should include selectedPackage if provided", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: true,
        userId: "user-123",
      });

      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
        fullName: "Test User",
        selectedPackage: "pro",
      });

      await registerModule.POST(request);

      expect(registerUser).toHaveBeenCalledWith(
        expect.any(String),
        expect.any(String),
        expect.any(String),
        "pro",
      );
    });

    it("should log successful registration", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: true,
        userId: "user-123",
      });

      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
        fullName: "Test User",
      });

      await registerModule.POST(request);

      expect(logger.info).toHaveBeenCalledWith("User registered successfully", {
        userId: "user-123",
      });
    });
  });

  describe("Validation Errors", () => {
    it("should return 400 if email is missing", async () => {
      const request = mockRequest({
        password: "SecurePass123!@#",
        fullName: "Test User",
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(400);
      expect(body.error).toBe("Missing required fields");
    });

    it("should return 400 if password is missing", async () => {
      const request = mockRequest({
        email: "test@example.com",
        fullName: "Test User",
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(400);
      expect(body.error).toBe("Missing required fields");
    });

    it("should return 400 if fullName is missing", async () => {
      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(400);
      expect(body.error).toBe("Missing required fields");
    });

    it("should return 400 for invalid email format", async () => {
      const request = mockRequest({
        email: "not-an-email",
        password: "SecurePass123!@#",
        fullName: "Test User",
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(400);
      expect(body.error).toBe("Invalid email format");
    });

    it("should return 400 for weak password", async () => {
      const request = mockRequest({
        email: "test@example.com",
        password: "weak", // Too short, no complexity
        fullName: "Test User",
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(400);
      expect(body.error).toContain("Password");
    });

    it("should return 400 for invalid name", async () => {
      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
        fullName: "A", // Too short
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(400);
      expect(body.error).toContain("Name");
    });
  });

  describe("Registration Failures", () => {
    it("should return 400 if user already exists", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: false,
        error: "User already exists",
      });

      const request = mockRequest({
        email: "existing@example.com",
        password: "SecurePass123!@#",
        fullName: "Test User",
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(400);
      expect(body.error).toBe("User already exists");
    });

    it("should return 400 on registration failure", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: false,
        error: "Database error",
      });

      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
        fullName: "Test User",
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(400);
      expect(body.error).toBe("Database error");
    });

    it("should return 500 on unexpected errors", async () => {
      (registerUser as jest.Mock).mockRejectedValue(
        new Error("Unexpected error"),
      );

      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
        fullName: "Test User",
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(500);
      expect(body.error).toBe("Internal server error");
      expect(logger.error).toHaveBeenCalledWith(
        "Registration API error",
        expect.any(Error),
      );
    });
  });

  describe("Input Sanitization", () => {
    it("should sanitize email input", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: true,
        userId: "user-123",
      });

      const request = mockRequest({
        email: "<script>test@example.com</script>",
        password: "SecurePass123!@#",
        fullName: "Test User",
      });

      await registerModule.POST(request);

      expect(registerUser).toHaveBeenCalledWith(
        expect.not.stringContaining("<script>"),
        expect.any(String),
        expect.any(String),
        expect.any(String),
      );
    });

    it("should sanitize name input", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: true,
        userId: "user-123",
      });

      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
        fullName: "<b>Test</b> User",
      });

      await registerModule.POST(request);

      expect(registerUser).toHaveBeenCalledWith(
        expect.any(String),
        expect.any(String),
        expect.not.stringContaining("<b>"),
        expect.any(String),
      );
    });

    it("should lowercase email", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: true,
        userId: "user-123",
      });

      const request = mockRequest({
        email: "TEST@EXAMPLE.COM",
        password: "SecurePass123!@#",
        fullName: "Test User",
      });

      await registerModule.POST(request);

      expect(registerUser).toHaveBeenCalledWith(
        "test@example.com",
        expect.any(String),
        expect.any(String),
        expect.any(String),
      );
    });
  });

  describe("Edge Cases", () => {
    it("should handle empty strings", async () => {
      const request = mockRequest({
        email: "",
        password: "",
        fullName: "",
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(400);
      expect(body.error).toBe("Missing required fields");
    });

    it("should handle whitespace-only inputs", async () => {
      const request = mockRequest({
        email: "   ",
        password: "   ",
        fullName: "   ",
      });

      const response = await registerModule.POST(request);

      expect(response.status).toBe(400);
    });

    it("should handle very long names", async () => {
      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
        fullName: "A".repeat(101), // > 100 chars
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(400);
      expect(body.error).toContain("Name");
    });

    it("should handle special characters in name", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: true,
        userId: "user-123",
      });

      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
        fullName: "O'Brien-Smith", // Valid special chars
      });

      const response = await registerModule.POST(request);

      expect(response.status).toBe(201);
    });

    it("should handle Arabic names", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: true,
        userId: "user-123",
      });

      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
        fullName: "محمد أحمد",
      });

      const response = await registerModule.POST(request);

      expect(response.status).toBe(201);
    });
  });

  describe("Security Requirements", () => {
    it("should validate password complexity", async () => {
      const weakPasswords = [
        "short",
        "nocapitals123!",
        "NOLOWERCASE123!",
        "NoNumbers!@#",
        "NoSpecialChar123",
        "Password123!", // Common password
      ];

      for (const password of weakPasswords) {
        jest.clearAllMocks();

        const request = mockRequest({
          email: "test@example.com",
          password,
          fullName: "Test User",
        });

        const response = await registerModule.POST(request);

        expect(response.status).toBe(400);
      }
    });

    it("should require minimum 12 character password", async () => {
      const request = mockRequest({
        email: "test@example.com",
        password: "Short1!", // < 12 chars
        fullName: "Test User",
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(400);
      expect(body.error).toContain("12");
    });

    it("should prevent SQL injection in inputs", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: true,
        userId: "user-123",
      });

      const request = mockRequest({
        email: "test@example.com'; DROP TABLE users;--",
        password: "SecurePass123!@#",
        fullName: "Test User",
      });

      await registerModule.POST(request);

      // Email should be sanitized before being passed to registerUser
      const emailArg = (registerUser as jest.Mock).mock.calls[0][0];
      expect(emailArg).not.toContain("DROP TABLE");
      expect(emailArg).not.toContain(";--");
    });

    it("should prevent XSS in name field", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: true,
        userId: "user-123",
      });

      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
        fullName: '<script>alert("xss")</script>John',
      });

      await registerModule.POST(request);

      const nameArg = (registerUser as jest.Mock).mock.calls[0][2];
      expect(nameArg).not.toContain("<script>");
      expect(nameArg).not.toContain("alert");
    });
  });

  describe("Package Selection", () => {
    it("should accept selectedPackage parameter", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: true,
        userId: "user-123",
      });

      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
        fullName: "Test User",
        selectedPackage: "tier2",
      });

      await registerModule.POST(request);

      expect(registerUser).toHaveBeenCalledWith(
        expect.any(String),
        expect.any(String),
        expect.any(String),
        "tier2",
      );
    });

    it("should pass undefined if no package selected", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: true,
        userId: "user-123",
      });

      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
        fullName: "Test User",
      });

      await registerModule.POST(request);

      expect(registerUser).toHaveBeenCalledWith(
        expect.any(String),
        expect.any(String),
        expect.any(String),
        undefined,
      );
    });
  });

  describe("Error Handling", () => {
    it("should handle registerUser returning error", async () => {
      (registerUser as jest.Mock).mockResolvedValue({
        success: false,
        error: "User already exists",
      });

      const request = mockRequest({
        email: "existing@example.com",
        password: "SecurePass123!@#",
        fullName: "Test User",
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(400);
      expect(body.error).toBe("User already exists");
    });

    it("should handle registerUser throwing exception", async () => {
      (registerUser as jest.Mock).mockRejectedValue(
        new Error("Database connection failed"),
      );

      const request = mockRequest({
        email: "test@example.com",
        password: "SecurePass123!@#",
        fullName: "Test User",
      });

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(500);
      expect(body.error).toBe("Internal server error");
      expect(logger.error).toHaveBeenCalled();
    });

    it("should handle JSON parsing errors", async () => {
      const request = {
        json: jest.fn().mockRejectedValue(new Error("Invalid JSON")),
        headers: { get: jest.fn(() => null) },
      } as unknown as NextRequest;

      const response = await registerModule.POST(request);
      const body = await response.json();

      expect(response.status).toBe(500);
      expect(logger.error).toHaveBeenCalled();
    });
  });

  describe("Integration with Middleware", () => {
    it("should be wrapped with CSRF protection", () => {
      // The POST export should be the result of withCSRF
      expect(registerModule.POST).toBeDefined();
    });

    it("should be wrapped with rate limiting", () => {
      // The POST export should be the result of withRateLimit
      expect(registerModule.POST).toBeDefined();
    });
  });
});
