import {
  validateLink,
  validateExternalLink,
  validateDeepLink,
  isDeepLink,
} from "../utils";
import { isUrl } from "../../Line/Container/Wrapper/utils";

// Mock the isUrl utility function
jest.mock("../../Line/Container/Wrapper/utils", () => ({
  isUrl: jest.fn(),
}));

describe("utils.js", () => {
  const mockFormatMessage = jest.fn();
  const mockMessages = {
    invalidUrl: {
      id: "invalidUrl",
      defaultMessage: "Invalid URL format",
    },
  };

  beforeEach(() => {
    jest.clearAllMocks();
    mockFormatMessage.mockImplementation((msg) => msg.defaultMessage);
  });

  describe("validateLink", () => {
    describe("Valid scenarios", () => {
      it("should return null for valid URL", () => {
        isUrl.mockReturnValue(true);

        const result = validateLink("https://example.com", mockFormatMessage, mockMessages);

        expect(result).toBeNull();
        expect(isUrl).toHaveBeenCalledWith("https://example.com");
        expect(mockFormatMessage).not.toHaveBeenCalled();
      });

      it("should return null for valid URL with whitespace", () => {
        isUrl.mockReturnValue(true);

        const result = validateLink("  https://example.com  ", mockFormatMessage, mockMessages);

        expect(result).toBeNull();
        expect(isUrl).toHaveBeenCalledWith("https://example.com");
        expect(mockFormatMessage).not.toHaveBeenCalled();
      });

      it("should return null for empty URL", () => {
        const result = validateLink("", mockFormatMessage, mockMessages);

        expect(result).toBeNull();
        expect(isUrl).not.toHaveBeenCalled();
        expect(mockFormatMessage).not.toHaveBeenCalled();
      });

      it("should return null for null URL", () => {
        const result = validateLink(null, mockFormatMessage, mockMessages);

        expect(result).toBeNull();
        expect(isUrl).not.toHaveBeenCalled();
        expect(mockFormatMessage).not.toHaveBeenCalled();
      });

      it("should return null for undefined URL", () => {
        const result = validateLink(undefined, mockFormatMessage, mockMessages);

        expect(result).toBeNull();
        expect(isUrl).not.toHaveBeenCalled();
        expect(mockFormatMessage).not.toHaveBeenCalled();
      });

      it("should return null for whitespace-only URL", () => {
        const result = validateLink("   ", mockFormatMessage, mockMessages);

        expect(result).toBeNull();
        expect(isUrl).not.toHaveBeenCalled();
        expect(mockFormatMessage).not.toHaveBeenCalled();
      });

      it("should return null for tab and newline whitespace", () => {
        const result = validateLink("\t\n\r", mockFormatMessage, mockMessages);

        expect(result).toBeNull();
        expect(isUrl).not.toHaveBeenCalled();
        expect(mockFormatMessage).not.toHaveBeenCalled();
      });
    });

    describe("Invalid scenarios", () => {
      it("should return error message for invalid URL", () => {
        isUrl.mockReturnValue(false);

        const result = validateLink("invalid-url", mockFormatMessage, mockMessages);

        expect(result).toBe("Invalid URL format");
        expect(isUrl).toHaveBeenCalledWith("invalid-url");
        expect(mockFormatMessage).toHaveBeenCalledWith(mockMessages.invalidUrl);
      });

      it("should return error message for invalid URL with whitespace", () => {
        isUrl.mockReturnValue(false);

        const result = validateLink("  invalid-url  ", mockFormatMessage, mockMessages);

        expect(result).toBe("Invalid URL format");
        expect(isUrl).toHaveBeenCalledWith("invalid-url");
        expect(mockFormatMessage).toHaveBeenCalledWith(mockMessages.invalidUrl);
      });

      it("should return error message for malformed URL", () => {
        isUrl.mockReturnValue(false);

        const result = validateLink("htp://malformed", mockFormatMessage, mockMessages);

        expect(result).toBe("Invalid URL format");
        expect(isUrl).toHaveBeenCalledWith("htp://malformed");
        expect(mockFormatMessage).toHaveBeenCalledWith(mockMessages.invalidUrl);
      });

      it("should return error message for URL without protocol", () => {
        isUrl.mockReturnValue(false);

        const result = validateLink("example.com", mockFormatMessage, mockMessages);

        expect(result).toBe("Invalid URL format");
        expect(isUrl).toHaveBeenCalledWith("example.com");
        expect(mockFormatMessage).toHaveBeenCalledWith(mockMessages.invalidUrl);
      });

      it("should handle special characters in invalid URL", () => {
        isUrl.mockReturnValue(false);

        const result = validateLink("http://ex@mple.com", mockFormatMessage, mockMessages);

        expect(result).toBe("Invalid URL format");
        expect(isUrl).toHaveBeenCalledWith("http://ex@mple.com");
        expect(mockFormatMessage).toHaveBeenCalledWith(mockMessages.invalidUrl);
      });
    });

    describe("Edge cases", () => {
      it("should handle custom formatMessage function", () => {
        const customFormatMessage = jest.fn().mockReturnValue("Custom error message");
        isUrl.mockReturnValue(false);

        const result = validateLink("invalid", customFormatMessage, mockMessages);

        expect(result).toBe("Custom error message");
        expect(customFormatMessage).toHaveBeenCalledWith(mockMessages.invalidUrl);
      });

      it("should handle different message object structure", () => {
        const customMessages = {
          invalidUrl: {
            id: "custom.invalidUrl",
            defaultMessage: "Custom invalid URL message",
          },
        };
        isUrl.mockReturnValue(false);

        const result = validateLink("invalid", mockFormatMessage, customMessages);

        expect(result).toBe("Custom invalid URL message");
        expect(mockFormatMessage).toHaveBeenCalledWith(customMessages.invalidUrl);
      });

      it("should trim very long whitespace strings", () => {
        const longWhitespace = " ".repeat(100);

        const result = validateLink(longWhitespace, mockFormatMessage, mockMessages);

        expect(result).toBeNull();
        expect(isUrl).not.toHaveBeenCalled();
      });
    });
  });

  describe("validateExternalLink", () => {
    it("should call validateLink with correct parameters for valid URL", () => {
      isUrl.mockReturnValue(true);

      const result = validateExternalLink("https://external.com", mockFormatMessage, mockMessages);

      expect(result).toBeNull();
      expect(isUrl).toHaveBeenCalledWith("https://external.com");
    });

    it("should call validateLink with correct parameters for invalid URL", () => {
      isUrl.mockReturnValue(false);

      const result = validateExternalLink("invalid-external", mockFormatMessage, mockMessages);

      expect(result).toBe("Invalid URL format");
      expect(isUrl).toHaveBeenCalledWith("invalid-external");
      expect(mockFormatMessage).toHaveBeenCalledWith(mockMessages.invalidUrl);
    });

    it("should handle empty external link", () => {
      const result = validateExternalLink("", mockFormatMessage, mockMessages);

      expect(result).toBeNull();
      expect(isUrl).not.toHaveBeenCalled();
    });

    it("should handle null external link", () => {
      const result = validateExternalLink(null, mockFormatMessage, mockMessages);

      expect(result).toBeNull();
      expect(isUrl).not.toHaveBeenCalled();
    });

    it("should trim whitespace in external link", () => {
      isUrl.mockReturnValue(true);

      const result = validateExternalLink("  https://external.com  ", mockFormatMessage, mockMessages);

      expect(result).toBeNull();
      expect(isUrl).toHaveBeenCalledWith("https://external.com");
    });
  });

  describe("validateDeepLink", () => {
    it("should return null for valid custom scheme deep link", () => {
      const result = validateDeepLink("myapp://deeplink", mockFormatMessage, mockMessages);

      expect(result).toBeNull();
      expect(mockFormatMessage).not.toHaveBeenCalled();
    });

    it("should return null for valid HTTP deep link", () => {
      const result = validateDeepLink("https://example.com/path", mockFormatMessage, mockMessages);

      expect(result).toBeNull();
      expect(mockFormatMessage).not.toHaveBeenCalled();
    });

    it("should return null for valid relative path deep link", () => {
      const result = validateDeepLink("/path/to/screen", mockFormatMessage, mockMessages);

      expect(result).toBeNull();
      expect(mockFormatMessage).not.toHaveBeenCalled();
    });

    it("should return error message for invalid deep link", () => {
      const result = validateDeepLink("invalid-deeplink", mockFormatMessage, mockMessages);

      expect(result).toBe("Invalid URL format");
      expect(mockFormatMessage).toHaveBeenCalledWith(mockMessages.invalidUrl);
    });

    it("should handle empty deep link", () => {
      const result = validateDeepLink("", mockFormatMessage, mockMessages);

      expect(result).toBeNull();
      expect(mockFormatMessage).not.toHaveBeenCalled();
    });

    it("should handle undefined deep link", () => {
      const result = validateDeepLink(undefined, mockFormatMessage, mockMessages);

      expect(result).toBeNull();
      expect(mockFormatMessage).not.toHaveBeenCalled();
    });

    it("should trim whitespace in deep link", () => {
      const result = validateDeepLink("  myapp://deeplink  ", mockFormatMessage, mockMessages);

      expect(result).toBeNull();
      expect(mockFormatMessage).not.toHaveBeenCalled();
    });

    it("should handle custom scheme deep links", () => {
      const result = validateDeepLink("customscheme://action/path", mockFormatMessage, mockMessages);

      expect(result).toBeNull();
      expect(mockFormatMessage).not.toHaveBeenCalled();
    });

    it("should handle app-specific deep links without path", () => {
      const result = validateDeepLink("myapp://", mockFormatMessage, mockMessages);

      expect(result).toBeNull();
      expect(mockFormatMessage).not.toHaveBeenCalled();
    });
  });

  describe("isDeepLink", () => {
    describe("Valid deep link formats", () => {
      it("should validate custom scheme URLs", () => {
        expect(isDeepLink("myapp://path")).toBe(true);
        expect(isDeepLink("com.example.app://action")).toBe(true);
        expect(isDeepLink("customscheme://path/to/screen")).toBe(true);
      });

      it("should validate HTTP/HTTPS URLs", () => {
        expect(isDeepLink("https://example.com/path")).toBe(true);
        expect(isDeepLink("http://example.com/path")).toBe(true);
        expect(isDeepLink("https://app.example.com/deep/link")).toBe(true);
      });

      it("should validate relative paths", () => {
        expect(isDeepLink("/path")).toBe(true);
        expect(isDeepLink("path")).toBe(true);
        expect(isDeepLink("/path/to/screen")).toBe(true);
        expect(isDeepLink("home/screen")).toBe(true);
      });

      it("should validate app-specific deep links without path", () => {
        expect(isDeepLink("myapp://")).toBe(true);
        expect(isDeepLink("com.example.app://")).toBe(true);
      });

      it("should handle URLs with query parameters", () => {
        expect(isDeepLink("myapp://path?param=value")).toBe(true);
        expect(isDeepLink("https://example.com/path?param=value")).toBe(true);
      });

      it("should handle URLs with special characters", () => {
        expect(isDeepLink("myapp://path-with-dashes")).toBe(true);
        expect(isDeepLink("myapp://path_with_underscores")).toBe(true);
        expect(isDeepLink("myapp://path.with.dots")).toBe(true);
      });
    });

    describe("Invalid deep link formats", () => {
      it("should reject invalid URLs", () => {
        expect(isDeepLink("invalid-url")).toBe(false);
        expect(isDeepLink("not-a-url")).toBe(false);
        expect(isDeepLink("random text")).toBe(false);
      });

      it("should reject URLs with invalid schemes", () => {
        expect(isDeepLink("://path")).toBe(false);
        expect(isDeepLink("123://path")).toBe(false);
        expect(isDeepLink("-invalid://path")).toBe(false);
      });

      it("should reject empty or whitespace-only strings", () => {
        expect(isDeepLink("")).toBe(false);
        expect(isDeepLink("   ")).toBe(false);
        expect(isDeepLink("\t\n")).toBe(false);
      });

      it("should reject null and undefined", () => {
        expect(isDeepLink(null)).toBe(false);
        expect(isDeepLink(undefined)).toBe(false);
      });

      it("should reject non-string inputs", () => {
        expect(isDeepLink(123)).toBe(false);
        expect(isDeepLink({})).toBe(false);
        expect(isDeepLink([])).toBe(false);
      });
    });

    describe("Edge cases", () => {
      it("should trim whitespace", () => {
        expect(isDeepLink("  myapp://path  ")).toBe(true);
        expect(isDeepLink("\tmyapp://path\n")).toBe(true);
      });

      it("should handle very long URLs", () => {
        const longPath = "/" + "a".repeat(1000);
        expect(isDeepLink(longPath)).toBe(true);
      });

      it("should handle URLs with multiple slashes", () => {
        expect(isDeepLink("myapp://path//to//screen")).toBe(true);
        expect(isDeepLink("///path")).toBe(true);
      });
    });
  });

  describe("Function independence", () => {
    it("should ensure validateExternalLink and validateDeepLink are independent", () => {
      isUrl.mockReturnValue(true);

      validateExternalLink("https://external.com", mockFormatMessage, mockMessages);
      validateDeepLink("myapp://deeplink", mockFormatMessage, mockMessages);

      expect(isUrl).toHaveBeenCalledTimes(1);
      expect(isUrl).toHaveBeenCalledWith("https://external.com");
    });

    it("should handle mixed valid and invalid calls", () => {
      isUrl.mockReturnValueOnce(true);

      const result1 = validateExternalLink("https://valid.com", mockFormatMessage, mockMessages);
      const result2 = validateDeepLink("invalid", mockFormatMessage, mockMessages);

      expect(result1).toBeNull();
      expect(result2).toBe("Invalid URL format");
      expect(mockFormatMessage).toHaveBeenCalledTimes(1);
    });
  });

  describe("Error handling", () => {
    it("should handle isUrl throwing an error", () => {
      isUrl.mockImplementation(() => {
        throw new Error("isUrl error");
      });

      expect(() => {
        validateLink("https://example.com", mockFormatMessage, mockMessages);
      }).toThrow("isUrl error");
    });

    it("should handle formatMessage throwing an error", () => {
      isUrl.mockReturnValue(false);
      mockFormatMessage.mockImplementation(() => {
        throw new Error("formatMessage error");
      });

      expect(() => {
        validateLink("invalid", mockFormatMessage, mockMessages);
      }).toThrow("formatMessage error");
    });
  });
});
