/**
 * Demo Booking Form - Required Fields Validation Tests
 *
 * These tests ensure consistency between frontend and backend validation
 * to prevent "Missing required fields" errors on form submission.
 *
 * BUG PREVENTED: Frontend marks phone as optional, but API requires it,
 * causing booking failures with "Missing required fields" error.
 */

import { describe, test, expect, beforeEach, jest } from "@jest/globals";

// Frontend validation functions
import {
  validateEmail,
  validatePhoneNumber,
  validateName,
  validateMessage,
} from "@/components/DemoScheduler/utils/validationHelpers";

// Types
import type {
  BookingData,
  UserInfo,
  FormErrors,
} from "@/components/DemoScheduler/types/scheduler.types";

describe("Demo Booking - Required Fields Consistency", () => {
  /**
   * These tests document the expected required fields for demo booking.
   * If any of these tests fail, it indicates a mismatch between
   * frontend validation and backend API requirements.
   */

  describe("Backend API Required Fields", () => {
    // Document what the API expects (from /api/demo/book/route.ts line 139)
    const API_REQUIRED_FIELDS = [
      "fullName",
      "email",
      "telephone", // Phone is REQUIRED by API
      "appointmentDate",
      "appointmentTime",
      "duration",
    ];

    test("should have telephone as a required field in API", () => {
      // This test documents that telephone IS required by the API
      // If this expectation changes, update both frontend and backend
      expect(API_REQUIRED_FIELDS).toContain("telephone");
    });

    test("should require all essential booking information", () => {
      expect(API_REQUIRED_FIELDS).toContain("fullName");
      expect(API_REQUIRED_FIELDS).toContain("email");
      expect(API_REQUIRED_FIELDS).toContain("appointmentDate");
      expect(API_REQUIRED_FIELDS).toContain("appointmentTime");
      expect(API_REQUIRED_FIELDS).toContain("duration");
    });
  });

  describe("Frontend Form Validation", () => {
    test("name validation - required", () => {
      const emptyResult = validateName("");
      expect(emptyResult.valid).toBe(false);
      expect(emptyResult.error).toBeDefined();

      const validResult = validateName("John Doe");
      expect(validResult.valid).toBe(true);
    });

    test("email validation - required", () => {
      const emptyResult = validateEmail("");
      expect(emptyResult.valid).toBe(false);
      expect(emptyResult.error).toBeDefined();

      const invalidResult = validateEmail("not-an-email");
      expect(invalidResult.valid).toBe(false);

      const validResult = validateEmail("test@example.com");
      expect(validResult.valid).toBe(true);
    });

    test("phone validation - validates format when provided", () => {
      // When phone is provided, it should be validated
      const shortResult = validatePhoneNumber("+1234");
      expect(shortResult.valid).toBe(false);

      const validResult = validatePhoneNumber("+97412345678");
      expect(validResult.valid).toBe(true);
    });

    test("message validation - required", () => {
      const emptyResult = validateMessage("");
      expect(emptyResult.valid).toBe(false);
      expect(emptyResult.error).toBeDefined();

      const shortResult = validateMessage("Hi");
      expect(shortResult.valid).toBe(false);

      const validResult = validateMessage(
        "I would like to schedule a demo to see how Mawidi works.",
      );
      expect(validResult.valid).toBe(true);
    });
  });

  describe("Complete Booking Data Validation", () => {
    /**
     * Simulates the validation logic in useBookingForm.tsx
     * to test that all required fields are properly validated
     */
    function validateBookingData(
      bookingData: Partial<BookingData>,
    ): FormErrors {
      const errors: FormErrors = {};

      // Validate duration
      if (!bookingData.meetingDuration) {
        errors.duration = "Please select a meeting duration";
      }

      // Validate date
      if (!bookingData.selectedDate) {
        errors.date = "Please select a date";
      }

      // Validate time
      if (!bookingData.selectedTime) {
        errors.time = "Please select a time slot";
      }

      // Validate user info
      const userInfo = (bookingData as BookingData).userInfo;
      if (!userInfo) {
        errors.name = "Please provide your information";
        return errors;
      }

      // Name validation
      const nameResult = validateName(userInfo.name || "");
      if (!nameResult.valid) {
        errors.name = nameResult.error;
      }

      // Email validation
      const emailResult = validateEmail(userInfo.email || "");
      if (!emailResult.valid) {
        errors.email = emailResult.error;
      }

      // Phone validation - should be REQUIRED to match API
      if (!userInfo.phone) {
        errors.phone = "Phone number is required";
      } else {
        const phoneResult = validatePhoneNumber(userInfo.phone);
        if (!phoneResult.valid) {
          errors.phone = phoneResult.error;
        }
      }

      // Message validation
      const messageResult = validateMessage(userInfo.message || "");
      if (!messageResult.valid) {
        errors.message = messageResult.error;
      }

      return errors;
    }

    test("should reject booking without phone number", () => {
      const incompleteBooking: Partial<BookingData> = {
        meetingDuration: 30,
        selectedDate: new Date("2025-01-15"),
        selectedTime: "14:00",
        timezone: "Asia/Qatar",
        userInfo: {
          name: "John Doe",
          email: "john@example.com",
          phone: "", // Missing phone
          message: "I want to learn about Mawidi features for my clinic.",
        },
      };

      const errors = validateBookingData(incompleteBooking);

      // Phone should be flagged as missing
      expect(errors.phone).toBeDefined();
      expect(Object.keys(errors).length).toBeGreaterThan(0);
    });

    test("should accept booking with all required fields", () => {
      const completeBooking: Partial<BookingData> = {
        meetingDuration: 30,
        selectedDate: new Date("2025-01-15"),
        selectedTime: "14:00",
        timezone: "Asia/Qatar",
        userInfo: {
          name: "John Doe",
          email: "john@example.com",
          phone: "+97412345678", // Phone provided
          message: "I want to learn about Mawidi features for my clinic.",
        },
      };

      const errors = validateBookingData(completeBooking);

      expect(Object.keys(errors).length).toBe(0);
    });

    test("should reject booking without name", () => {
      const booking: Partial<BookingData> = {
        meetingDuration: 30,
        selectedDate: new Date("2025-01-15"),
        selectedTime: "14:00",
        userInfo: {
          name: "",
          email: "john@example.com",
          phone: "+97412345678",
          message: "Test message for demo booking.",
        },
      };

      const errors = validateBookingData(booking);
      expect(errors.name).toBeDefined();
    });

    test("should reject booking without email", () => {
      const booking: Partial<BookingData> = {
        meetingDuration: 30,
        selectedDate: new Date("2025-01-15"),
        selectedTime: "14:00",
        userInfo: {
          name: "John Doe",
          email: "",
          phone: "+97412345678",
          message: "Test message for demo booking.",
        },
      };

      const errors = validateBookingData(booking);
      expect(errors.email).toBeDefined();
    });

    test("should reject booking without message", () => {
      const booking: Partial<BookingData> = {
        meetingDuration: 30,
        selectedDate: new Date("2025-01-15"),
        selectedTime: "14:00",
        userInfo: {
          name: "John Doe",
          email: "john@example.com",
          phone: "+97412345678",
          message: "",
        },
      };

      const errors = validateBookingData(booking);
      expect(errors.message).toBeDefined();
    });

    test("should reject booking without date", () => {
      const booking: Partial<BookingData> = {
        meetingDuration: 30,
        selectedDate: undefined,
        selectedTime: "14:00",
        userInfo: {
          name: "John Doe",
          email: "john@example.com",
          phone: "+97412345678",
          message: "Test message for demo booking.",
        },
      };

      const errors = validateBookingData(booking);
      expect(errors.date).toBeDefined();
    });

    test("should reject booking without time", () => {
      const booking: Partial<BookingData> = {
        meetingDuration: 30,
        selectedDate: new Date("2025-01-15"),
        selectedTime: undefined,
        userInfo: {
          name: "John Doe",
          email: "john@example.com",
          phone: "+97412345678",
          message: "Test message for demo booking.",
        },
      };

      const errors = validateBookingData(booking);
      expect(errors.time).toBeDefined();
    });

    test("should reject booking without duration", () => {
      const booking: Partial<BookingData> = {
        meetingDuration: undefined,
        selectedDate: new Date("2025-01-15"),
        selectedTime: "14:00",
        userInfo: {
          name: "John Doe",
          email: "john@example.com",
          phone: "+97412345678",
          message: "Test message for demo booking.",
        },
      };

      const errors = validateBookingData(booking);
      expect(errors.duration).toBeDefined();
    });
  });

  describe("API Submission Data Format", () => {
    /**
     * Tests that verify the data format sent to the API
     * matches what the backend expects
     */

    test("should format submission data correctly", () => {
      const bookingData: BookingData = {
        meetingDuration: 60,
        selectedDate: new Date("2025-01-15"),
        selectedTime: "14:00",
        timezone: "Asia/Qatar",
        userInfo: {
          name: "Ahmed Al-Mansouri",
          email: "ahmed@example.com",
          phone: "+97412345678",
          message: "Looking forward to the demo!",
        },
      };

      // Simulate the submission data transformation
      const durationMap: Record<number, string> = {
        30: "30min",
        60: "1hr",
        90: "1.5hr",
        120: "2hr",
      };

      const formatDateOnly = (date: Date): string => {
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, "0");
        const day = String(date.getDate()).padStart(2, "0");
        return `${year}-${month}-${day}`;
      };

      const submissionData = {
        fullName: bookingData.userInfo.name,
        email: bookingData.userInfo.email,
        telephone: bookingData.userInfo.phone,
        appointmentDate: formatDateOnly(bookingData.selectedDate),
        appointmentTime: bookingData.selectedTime,
        duration: durationMap[bookingData.meetingDuration],
        specialRequirements: bookingData.userInfo.message,
        language: "en",
        timezone: bookingData.timezone,
      };

      // Verify all required fields are present
      expect(submissionData.fullName).toBe("Ahmed Al-Mansouri");
      expect(submissionData.email).toBe("ahmed@example.com");
      expect(submissionData.telephone).toBe("+97412345678");
      expect(submissionData.appointmentDate).toBe("2025-01-15");
      expect(submissionData.appointmentTime).toBe("14:00");
      expect(submissionData.duration).toBe("1hr");
      expect(submissionData.language).toBe("en");
    });

    test("should handle empty phone correctly", () => {
      const bookingData: Partial<BookingData> = {
        userInfo: {
          name: "John Doe",
          email: "john@example.com",
          phone: "", // Empty phone
          message: "Test message",
        },
      };

      // When phone is empty, submission should use empty string
      const telephone = bookingData.userInfo?.phone || "";

      // This will cause "Missing required fields" error from API
      // because API checks: !telephone (empty string is falsy)
      expect(telephone).toBe("");
      expect(!telephone).toBe(true); // This is what API checks
    });
  });
});

describe("Demo Booking - Error Message Clarity", () => {
  test("should provide specific error message for missing phone", () => {
    // When phone is missing, the error message should be specific
    const expectedError = "Phone number is required";
    expect(expectedError).not.toBe("Missing required fields");
  });

  test("should provide specific error messages for each missing field", () => {
    const fieldErrors = {
      name: "Name is required",
      email: "Email is required",
      phone: "Phone number is required",
      message: "Message is required",
      date: "Please select a date",
      time: "Please select a time slot",
      duration: "Please select a meeting duration",
    };

    // Each error should be specific, not generic
    Object.values(fieldErrors).forEach((error) => {
      expect(error).not.toBe("Missing required fields");
      expect(error.length).toBeGreaterThan(0);
    });
  });
});

describe("Demo Booking - Phone Field UI Consistency", () => {
  /**
   * These tests document the expected UI behavior.
   * The phone field label and validation must be consistent.
   */

  test("phone field should be marked as required in UI", () => {
    // Document expected behavior:
    // The UI label should NOT say "(Optional)" if phone is required by API
    const phoneLabel = "Phone Number";
    const optionalSuffix = "(Optional)";

    // If API requires phone, UI should NOT mark it as optional
    const apiRequiresPhone = true; // As documented in API tests above

    if (apiRequiresPhone) {
      // This test will fail if UI still shows "(Optional)"
      // indicating the bug needs to be fixed
      expect(phoneLabel).not.toContain(optionalSuffix);
    }
  });

  test("phone field should show asterisk if required", () => {
    // Required fields in the form show an asterisk (*)
    const requiredFields = ["name", "email", "message"];

    // If phone is required by API, it should also have asterisk
    const apiRequiresPhone = true;

    if (apiRequiresPhone) {
      requiredFields.push("phone");
    }

    expect(requiredFields).toContain("phone");
  });
});
