import "@testing-library/jest-dom";

// Mock the services module
const mockSpotRateQuoteDetails = jest.fn();
const mockAddCost = jest.fn();
const mockCustomerLead = jest.fn();
const mockAddRatesNotificationCarrier = jest.fn();

jest.mock("../services/public", () => ({
  SpotRateQuoteDetails: mockSpotRateQuoteDetails,
  addCost: mockAddCost,
  CustomerLead: mockCustomerLead,
  AddRatesNotificationCarrier: mockAddRatesNotificationCarrier,
}));

describe("getSpotDetails Function Tests", () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  test("should call SpotRateQuoteDetails with correct parameters", async () => {
    // Arrange
    const mockResponse = {
      status: "success",
      data: {
        id: 456,
        carrier_id: 789,
        linehaul: 1000,
        chessis_price: 200,
        assential_service: [
          {
            id: 1,
            cost: 50,
            original_cost: 45,
            name: "Test Service",
            required_for_move: 1,
          },
        ],
      },
    };

    mockSpotRateQuoteDetails.mockResolvedValue(mockResponse);

    // Create a mock getSpotDetails function that simulates the actual implementation
    const getSpotDetails = async (id, carrier_id) => {
      const token = "test-token";
      const queryId = "123";
      
      const response = await mockSpotRateQuoteDetails(token, queryId, id);
      
      if (response.status === "success") {
        if (response?.data?.assential_service?.length) {
          const clean = (arr) => arr.filter((v) => v != null && v !== "").join(",");
          const services = response.data.assential_service;
          response.data.accessorial_id = clean(services.map((item) => item?.id));
          response.data.accessorial_cost = clean(services.map((item) => item?.cost));
          response.data.accessorial_original_cost = clean(services.map((item) => item?.original_cost));
          response.data.special_services = clean(services.map((item) => item?.name));
          response.data.required_for_move = clean(services.map((item) => item?.required_for_move));
        }
      }
      
      return response.data;
    };

    // Act
    const result = await getSpotDetails(456, 789);

    // Assert
    expect(mockSpotRateQuoteDetails).toHaveBeenCalledWith("test-token", "123", 456);
    expect(result.accessorial_id).toBe("1");
    expect(result.accessorial_cost).toBe("50");
    expect(result.accessorial_original_cost).toBe("45");
    expect(result.special_services).toBe("Test Service");
    expect(result.required_for_move).toBe("1");
  });

  test("should handle empty essential services", async () => {
    // Arrange
    const mockResponse = {
      status: "success",
      data: {
        id: 456,
        carrier_id: 789,
        assential_service: [],
      },
    };

    mockSpotRateQuoteDetails.mockResolvedValue(mockResponse);

    const getSpotDetails = async (id, carrier_id) => {
      const token = "test-token";
      const queryId = "123";
      
      const response = await mockSpotRateQuoteDetails(token, queryId, id);
      
      if (response.status === "success") {
        if (response?.data?.assential_service?.length) {
          // This block should not execute with empty array
          const clean = (arr) => arr.filter((v) => v != null && v !== "").join(",");
          const services = response.data.assential_service;
          response.data.accessorial_id = clean(services.map((item) => item?.id));
        }
      }
      
      return response.data;
    };

    // Act
    const result = await getSpotDetails(456, 789);

    // Assert
    expect(mockSpotRateQuoteDetails).toHaveBeenCalledWith("test-token", "123", 456);
    expect(result.accessorial_id).toBeUndefined();
    expect(result.assential_service).toEqual([]);
  });

  test("should filter out null and empty values from essential services", async () => {
    // Arrange
    const mockResponse = {
      status: "success",
      data: {
        id: 456,
        carrier_id: 789,
        assential_service: [
          {
            id: 1,
            cost: 100,
            original_cost: 90,
            name: "Detention",
            required_for_move: 1,
          },
          {
            id: null, // Should be filtered out
            cost: null,
            original_cost: null,
            name: "",
            required_for_move: 0,
          },
          {
            id: 2,
            cost: 75,
            original_cost: 70,
            name: "Fuel Surcharge",
            required_for_move: 0,
          },
        ],
      },
    };

    mockSpotRateQuoteDetails.mockResolvedValue(mockResponse);

    const getSpotDetails = async (id, carrier_id) => {
      const token = "test-token";
      const queryId = "123";
      
      const response = await mockSpotRateQuoteDetails(token, queryId, id);
      
      if (response.status === "success") {
        if (response?.data?.assential_service?.length) {
          const clean = (arr) => arr.filter((v) => v != null && v !== "").join(",");
          const services = response.data.assential_service;
          response.data.accessorial_id = clean(services.map((item) => item?.id));
          response.data.accessorial_cost = clean(services.map((item) => item?.cost));
          response.data.accessorial_original_cost = clean(services.map((item) => item?.original_cost));
          response.data.special_services = clean(services.map((item) => item?.name));
          response.data.required_for_move = clean(services.map((item) => item?.required_for_move));
        }
      }
      
      return response.data;
    };

    // Act
    const result = await getSpotDetails(456, 789);

    // Assert
    expect(result.accessorial_id).toBe("1,2"); // null value filtered out
    expect(result.accessorial_cost).toBe("100,75");
    expect(result.accessorial_original_cost).toBe("90,70");
    expect(result.special_services).toBe("Detention,Fuel Surcharge"); // empty string filtered out
    expect(result.required_for_move).toBe("1,0,0"); // includes the 0 from the null object
  });

  test("should handle API errors gracefully", async () => {
    // Arrange
    mockSpotRateQuoteDetails.mockRejectedValue(new Error("API Error"));

    const getSpotDetails = async (id, carrier_id) => {
      try {
        const token = "test-token";
        const queryId = "123";
        
        const response = await mockSpotRateQuoteDetails(token, queryId, id);
        return response.data;
      } catch (error) {
        // Handle error gracefully
        return null;
      }
    };

    // Act
    const result = await getSpotDetails(456, 789);

    // Assert
    expect(mockSpotRateQuoteDetails).toHaveBeenCalled();
    expect(result).toBeNull();
  });
});

describe("SubmitRateModal handleSubmit Function Tests", () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  test("should validate required fields and return errors", async () => {
    // Simulate handleSubmit function validation logic
    const validateForm = (linehaul, fsc, chassis) => {
      let isValid = true;
      const errors = {};

      if (!linehaul) {
        errors.linehaul = "Linehaul is required";
        isValid = false;
      } else if (isNaN(parseFloat(linehaul)) || !isFinite(linehaul)) {
        errors.linehaul = "Linehaul must be a valid number";
        isValid = false;
      }

      if (!fsc) {
        errors.fsc = "FSC is required";
        isValid = false;
      } else if (isNaN(parseFloat(fsc)) || !isFinite(fsc)) {
        errors.fsc = "FSC must be a valid number";
        isValid = false;
      }

      if (!chassis) {
        errors.chassis = "Chassis is required";
        isValid = false;
      } else if (isNaN(parseFloat(chassis)) || !isFinite(chassis)) {
        errors.chassis = "Chassis must be a valid number";
        isValid = false;
      }

      return { isValid, errors };
    };

    // Test with empty values
    const result = validateForm("", "", "");

    expect(result.isValid).toBe(false);
    expect(result.errors.linehaul).toBe("Linehaul is required");
    expect(result.errors.fsc).toBe("FSC is required");
    expect(result.errors.chassis).toBe("Chassis is required");
  });

  test("should validate customer notes length", () => {
    const validateCustomerNotes = (customerNotes) => {
      const trimmedValue = typeof customerNotes === 'string' ? customerNotes.trim() : '';
      if (trimmedValue.length > 600) {
        return "Customer notes cannot exceed 600 characters.";
      }
      return "";
    };

    // Test with valid length
    const validNotes = "This is a valid note";
    expect(validateCustomerNotes(validNotes)).toBe("");

    // Test with exceeding length
    const longNotes = "a".repeat(601);
    expect(validateCustomerNotes(longNotes)).toBe("Customer notes cannot exceed 600 characters.");
  });

  test("should validate email format", () => {
    const validateEmail = (email) => {
      const emailsArray = (email || "").split(",").map(e => e.trim()).filter(e => e !== "");
      const emailRegex = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$/;
      const invalidEmails = emailsArray.filter(emailReg => !emailRegex.test(emailReg));

      if (invalidEmails.length > 0) {
        return "Please enter valid email address.";
      }
      return "";
    };

    // Test valid single email
    expect(validateEmail("test@example.com")).toBe("");

    // Test valid multiple emails
    expect(validateEmail("test1@example.com, test2@example.com")).toBe("");

    // Test invalid email
    expect(validateEmail("invalid-email")).toBe("Please enter valid email address.");

    // Test mix of valid and invalid emails
    expect(validateEmail("valid@example.com, invalid-email")).toBe("Please enter valid email address.");
  });

  test("should successfully submit with valid data", async () => {
    // Mock successful responses
    mockAddRatesNotificationCarrier.mockResolvedValue({ status: "success" });
    mockAddCost.mockResolvedValue({
      status: "success",
      message: "Rate submitted successfully",
    });

    // Simulate handleSubmit function logic
    const handleSubmit = async (formData) => {
      const { linehaul, fsc, chassis, customerNotes, accessorialCost, accessorialId, accessorialName } = formData;

      // Validate form
      const validateForm = (linehaul, fsc, chassis) => {
        return !(!linehaul || !fsc || !chassis);
      };

      const validateCustomerNotes = (notes) => {
        return notes.trim().length <= 600;
      };

      if (!validateForm(linehaul, fsc, chassis)) {
        return { success: false, error: "Validation failed" };
      }

      if (!validateCustomerNotes(customerNotes)) {
        return { success: false, error: "Customer notes too long" };
      }

      // Send notification
      const notifyPayload = {
        user_id: 1,
        quote_id: "123",
        type: "rate_select",
        carrier_id: "789",
        rate_id: 456,
      };

      await mockAddRatesNotificationCarrier("test-token", notifyPayload);

      // Submit rate
      const payload = {
        rate_id: 456,
        linehaul,
        fuelSurcharge: fsc,
        chassiss: chassis,
        accessorial_cost: accessorialCost,
        accessorial_id: accessorialId,
        accessorialName,
        emailsArray: ["test@example.com"],
        estimatedCost: 1500,
        grossValue: 250,
      };

      const response = await mockAddCost("test-token", payload, "123", [], [], customerNotes);
      
      return {
        success: response.status === "success",
        message: response.message,
      };
    };

    // Act
    const result = await handleSubmit({
      linehaul: "1200",
      fsc: "18",
      chassis: "250",
      customerNotes: "Valid notes",
      accessorialCost: [100],
      accessorialId: [1],
      accessorialName: ["Detention"],
    });

    // Assert
    expect(result.success).toBe(true);
    expect(result.message).toBe("Rate submitted successfully");
    expect(mockAddRatesNotificationCarrier).toHaveBeenCalledWith(
      "test-token",
      {
        user_id: 1,
        quote_id: "123",
        type: "rate_select",
        carrier_id: "789",
        rate_id: 456,
      }
    );
    expect(mockAddCost).toHaveBeenCalled();
  });

  test("should handle API error during submission", async () => {
    // Mock API error
    mockAddRatesNotificationCarrier.mockResolvedValue({ status: "success" });
    mockAddCost.mockResolvedValue({
      status: "error",
      message: "Submission failed",
    });

    const handleSubmit = async (formData) => {
      const { linehaul, fsc, chassis, customerNotes } = formData;

      // Validate form (simplified)
      if (!linehaul || !fsc || !chassis) {
        return { success: false, error: "Validation failed" };
      }

      try {
        // Send notification
        await mockAddRatesNotificationCarrier("test-token", {});

        // Submit rate
        const response = await mockAddCost("test-token", {}, "123", [], [], customerNotes);
        
        return {
          success: response.status === "success",
          error: response.status === "error" ? response.message : null,
        };
      } catch (error) {
        return { success: false, error: error.message };
      }
    };

    // Act
    const result = await handleSubmit({
      linehaul: "1200",
      fsc: "18",
      chassis: "250",
      customerNotes: "Valid notes",
    });

    // Assert
    expect(result.success).toBe(false);
    expect(result.error).toBe("Submission failed");
    expect(mockAddCost).toHaveBeenCalled();
  });

  test("should parse multiple emails correctly", () => {
    const parseEmails = (emailString) => {
      return (emailString || "").split(",").map(e => e.trim()).filter(e => e !== "");
    };

    // Test single email
    expect(parseEmails("test@example.com")).toEqual(["test@example.com"]);

    // Test multiple emails
    expect(parseEmails("test1@example.com, test2@example.com, test3@example.com")).toEqual([
      "test1@example.com",
      "test2@example.com", 
      "test3@example.com"
    ]);

    // Test emails with extra spaces
    expect(parseEmails("  test1@example.com  ,  test2@example.com  ")).toEqual([
      "test1@example.com",
      "test2@example.com"
    ]);

    // Test empty string
    expect(parseEmails("")).toEqual([]);
  });
});