/**
 * @jest-environment node
 */

// Mock isomorphic-dompurify to avoid ESM dependency chain (jsdom → @exodus/bytes)
jest.mock("isomorphic-dompurify", () => ({
  __esModule: true,
  default: {
    sanitize: (input: string, _config?: unknown) => {
      // Mimic DOMPurify with ALLOWED_TAGS=[] — strip all HTML tags
      return input.replace(/<[^>]*>/g, "");
    },
  },
}));

import {
  sanitizeChatMessage,
  sanitizePhoneNumber,
  detectXSSPatterns,
} from "@/lib/sanitizers/chat.sanitizer";

// ---------------------------------------------------------------------------
// sanitizeChatMessage
// ---------------------------------------------------------------------------
describe("sanitizeChatMessage", () => {
  it("strips <script> tags (XSS)", () => {
    const input = 'Hello <script>alert("xss")</script> world';
    const result = sanitizeChatMessage(input);
    expect(result).not.toContain("<script");
    expect(result).not.toContain("</script>");
  });

  it("strips nested/obfuscated script tags", () => {
    const result = sanitizeChatMessage("<ScRiPt>document.cookie</ScRiPt>");
    expect(result).not.toMatch(/<script/i);
  });

  it("removes null bytes", () => {
    const input = "Hello\0World\0!";
    const result = sanitizeChatMessage(input);
    expect(result).not.toContain("\0");
    expect(result).toContain("Hello");
    expect(result).toContain("World");
  });

  it("blocks javascript: protocol", () => {
    const input = "Click javascript:alert(1)";
    const result = sanitizeChatMessage(input);
    expect(result).not.toContain("javascript:");
  });

  it("blocks javascript: protocol case-insensitive", () => {
    const input = "JaVaScRiPt:alert(1)";
    const result = sanitizeChatMessage(input);
    expect(result.toLowerCase()).not.toContain("javascript:");
  });

  it("truncates messages longer than 10 000 characters", () => {
    const input = "A".repeat(15000);
    const result = sanitizeChatMessage(input);
    expect(result.length).toBeLessThanOrEqual(10000);
  });

  it("returns empty string for null input", () => {
    expect(sanitizeChatMessage(null)).toBe("");
  });

  it("returns empty string for undefined input", () => {
    expect(sanitizeChatMessage(undefined)).toBe("");
  });

  it("returns empty string for empty string input", () => {
    expect(sanitizeChatMessage("")).toBe("");
  });

  it("preserves normal text", () => {
    const input = "Hello, how are you today?";
    expect(sanitizeChatMessage(input)).toBe("Hello, how are you today?");
  });

  it("strips HTML tags but keeps text content", () => {
    const input = "<b>bold</b> and <i>italic</i>";
    const result = sanitizeChatMessage(input);
    expect(result).not.toContain("<b>");
    expect(result).not.toContain("<i>");
    expect(result).toContain("bold");
    expect(result).toContain("italic");
  });

  it("replaces data: URIs with data-blocked:", () => {
    const input = "data:text/html,<h1>XSS</h1>";
    const result = sanitizeChatMessage(input);
    expect(result).not.toMatch(/^data:/);
  });

  it("removes vbscript: protocol", () => {
    const input = 'vbscript:MsgBox("XSS")';
    const result = sanitizeChatMessage(input);
    expect(result).not.toContain("vbscript:");
  });

  it("normalises excessive newlines to max two consecutive", () => {
    const input = "Line1\n\n\n\n\nLine2";
    const result = sanitizeChatMessage(input);
    expect(result).not.toContain("\n\n\n");
    expect(result).toContain("Line1\n\nLine2");
  });
});

// ---------------------------------------------------------------------------
// sanitizePhoneNumber
// ---------------------------------------------------------------------------
describe("sanitizePhoneNumber", () => {
  it("keeps digits, +, -, (, ), and spaces", () => {
    const input = "+1 (555) 123-4567";
    expect(sanitizePhoneNumber(input)).toBe("+1 (555) 123-4567");
  });

  it("strips letters and special characters", () => {
    const input = "+1-555-CALL-ME!@#";
    const result = sanitizePhoneNumber(input);
    expect(result).not.toMatch(/[A-Za-z!@#]/);
    // digits, +, - should remain
    expect(result).toContain("+1-555-");
  });

  it("truncates to max 30 characters", () => {
    const input = "+" + "1".repeat(50);
    const result = sanitizePhoneNumber(input);
    expect(result.length).toBeLessThanOrEqual(30);
  });

  it("returns empty string for null", () => {
    expect(sanitizePhoneNumber(null)).toBe("");
  });

  it("returns empty string for undefined", () => {
    expect(sanitizePhoneNumber(undefined)).toBe("");
  });

  it("strips HTML from phone numbers", () => {
    // Note: truncation (30 chars) happens before character filtering,
    // so only chars surviving both steps are kept
    const input = "+974555<b>hi</b>";
    const result = sanitizePhoneNumber(input);
    expect(result).not.toContain("<");
    expect(result).not.toContain(">");
    expect(result).toContain("+974555");
  });
});

// ---------------------------------------------------------------------------
// detectXSSPatterns
// ---------------------------------------------------------------------------
describe("detectXSSPatterns", () => {
  it("detects script_tag pattern", () => {
    const result = detectXSSPatterns("<script>alert(1)</script>");
    expect(result.isSuspicious).toBe(true);
    expect(result.patterns).toContain("script_tag");
  });

  it("detects event_handler pattern", () => {
    const result = detectXSSPatterns('<img onerror="alert(1)">');
    expect(result.isSuspicious).toBe(true);
    expect(result.patterns).toContain("event_handler");
  });

  it("detects javascript_protocol pattern", () => {
    const result = detectXSSPatterns("javascript:void(0)");
    expect(result.isSuspicious).toBe(true);
    expect(result.patterns).toContain("javascript_protocol");
  });

  it("detects data_uri_html pattern", () => {
    const result = detectXSSPatterns("data:text/html,<h1>XSS</h1>");
    expect(result.isSuspicious).toBe(true);
    expect(result.patterns).toContain("data_uri_html");
  });

  it("detects svg_onload pattern", () => {
    const result = detectXSSPatterns('<svg onload="alert(1)">');
    expect(result.isSuspicious).toBe(true);
    expect(result.patterns).toContain("svg_onload");
  });

  it("returns isSuspicious=false for clean content", () => {
    const result = detectXSSPatterns("Hello, this is a normal message.");
    expect(result.isSuspicious).toBe(false);
    expect(result.patterns).toHaveLength(0);
  });

  it("detects multiple patterns simultaneously", () => {
    const input = "<script>x</script> javascript:x <img onerror=x>";
    const result = detectXSSPatterns(input);
    expect(result.isSuspicious).toBe(true);
    expect(result.patterns).toContain("script_tag");
    expect(result.patterns).toContain("javascript_protocol");
    expect(result.patterns).toContain("event_handler");
    expect(result.patterns.length).toBeGreaterThanOrEqual(3);
  });

  it("detects css_expression pattern", () => {
    const result = detectXSSPatterns("background: expression(alert(1))");
    expect(result.isSuspicious).toBe(true);
    expect(result.patterns).toContain("css_expression");
  });
});
