package com.storycove.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.storycove.dto.HtmlSanitizationConfigDto;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.*;

/**
 * Security-critical tests for HtmlSanitizationService.
 * These tests ensure that malicious HTML is properly sanitized.
 */
@SpringBootTest
class HtmlSanitizationServiceTest {

    @Autowired
    private HtmlSanitizationService sanitizationService;

    @BeforeEach
    void setUp() {
        // Service is initialized via @PostConstruct
    }

    // ========================================
    // XSS Attack Prevention Tests
    // ========================================

    @Test
    @DisplayName("Should remove script tags (XSS prevention)")
    void testRemoveScriptTags() {
        String malicious = "<p>Hello</p><script>alert('XSS')</script>";
        String sanitized = sanitizationService.sanitize(malicious);

        assertFalse(sanitized.contains("<script>"));
        assertFalse(sanitized.contains("alert"));
        assertTrue(sanitized.contains("Hello"));
    }

    @Test
    @DisplayName("Should remove inline JavaScript event handlers")
    void testRemoveEventHandlers() {
        String malicious = "<p onclick='alert(\"XSS\")'>Click me</p>";
        String sanitized = sanitizationService.sanitize(malicious);

        assertFalse(sanitized.contains("onclick"));
        assertFalse(sanitized.contains("alert"));
        assertTrue(sanitized.contains("Click me"));
    }

    @Test
    @DisplayName("Should remove javascript: URLs")
    void testRemoveJavaScriptUrls() {
        String malicious = "<a href='javascript:alert(\"XSS\")'>Click</a>";
        String sanitized = sanitizationService.sanitize(malicious);

        assertFalse(sanitized.contains("javascript:"));
        assertFalse(sanitized.contains("alert"));
    }

    @Test
    @DisplayName("Should remove data: URLs with JavaScript")
    void testRemoveDataUrlsWithJs() {
        String malicious = "<a href='data:text/html,<script>alert(\"XSS\")</script>'>Click</a>";
        String sanitized = sanitizationService.sanitize(malicious);

        assertFalse(sanitized.toLowerCase().contains("script"));
    }

    @Test
    @DisplayName("Should remove iframe tags")
    void testRemoveIframeTags() {
        String malicious = "<p>Content</p><iframe src='http://evil.com'></iframe>";
        String sanitized = sanitizationService.sanitize(malicious);

        assertFalse(sanitized.contains("<iframe"));
        assertTrue(sanitized.contains("Content"));
    }

    @Test
    @DisplayName("Should remove object and embed tags")
    void testRemoveObjectAndEmbedTags() {
        String malicious = "<object data='http://evil.com'></object><embed src='http://evil.com'>";
        String sanitized = sanitizationService.sanitize(malicious);

        assertFalse(sanitized.contains("<object"));
        assertFalse(sanitized.contains("<embed"));
    }

    // ========================================
    // Allowed Content Tests
    // ========================================

    @Test
    @DisplayName("Should preserve safe HTML tags")
    void testPreserveSafeTags() {
        String safe = "<p>Paragraph</p><h1>Heading</h1><ul><li>Item</li></ul>";
        String sanitized = sanitizationService.sanitize(safe);

        assertTrue(sanitized.contains("<p>"));
        assertTrue(sanitized.contains("<h1>"));
        assertTrue(sanitized.contains("<ul>"));
        assertTrue(sanitized.contains("<li>"));
        assertTrue(sanitized.contains("Paragraph"));
        assertTrue(sanitized.contains("Heading"));
    }

    @Test
    @DisplayName("Should preserve text formatting tags")
    void testPreserveFormattingTags() {
        String formatted = "<p><strong>Bold</strong> <em>Italic</em> <u>Underline</u></p>";
        String sanitized = sanitizationService.sanitize(formatted);

        assertTrue(sanitized.contains("<strong>"));
        assertTrue(sanitized.contains("<em>"));
        assertTrue(sanitized.contains("<u>"));
    }

    @Test
    @DisplayName("Should preserve safe links")
    void testPreserveSafeLinks() {
        String link = "<a href='https://example.com'>Link</a>";
        String sanitized = sanitizationService.sanitize(link);

        assertTrue(sanitized.contains("<a"));
        assertTrue(sanitized.contains("href"));
        assertTrue(sanitized.contains("example.com"));
    }

    @Test
    @DisplayName("Should preserve images with safe attributes")
    void testPreserveSafeImages() {
        String img = "<img src='https://example.com/image.jpg' alt='Description'>";
        String sanitized = sanitizationService.sanitize(img);

        assertTrue(sanitized.contains("<img"));
        assertTrue(sanitized.contains("src"));
        assertTrue(sanitized.contains("alt"));
    }

    @Test
    @DisplayName("Should preserve relative image URLs")
    void testPreserveRelativeImageUrls() {
        String img = "<img src='/images/photo.jpg' alt='Photo'>";
        String sanitized = sanitizationService.sanitize(img);

        assertTrue(sanitized.contains("<img"));
        assertTrue(sanitized.contains("/images/photo.jpg"));
    }

    // ========================================
    // Figure Tag Preprocessing Tests
    // ========================================

    @Test
    @DisplayName("Should extract image from figure tag")
    void testExtractImageFromFigure() {
        String figure = "<figure><img src='/image.jpg' alt='Test'><figcaption>Caption</figcaption></figure>";
        String sanitized = sanitizationService.sanitize(figure);

        assertFalse(sanitized.contains("<figure"));
        assertFalse(sanitized.contains("<figcaption"));
        assertTrue(sanitized.contains("<img"));
        assertTrue(sanitized.contains("/image.jpg"));
    }

    @Test
    @DisplayName("Should use figcaption as alt text if alt is missing")
    void testFigcaptionAsAltText() {
        String figure = "<figure><img src='/image.jpg'><figcaption>My Caption</figcaption></figure>";
        String sanitized = sanitizationService.sanitize(figure);

        assertTrue(sanitized.contains("<img"));
        assertTrue(sanitized.contains("alt="));
        assertTrue(sanitized.contains("My Caption"));
    }

    @Test
    @DisplayName("Should remove figure without images")
    void testRemoveFigureWithoutImages() {
        String figure = "<p>Before</p><figure><figcaption>Caption only</figcaption></figure><p>After</p>";
        String sanitized = sanitizationService.sanitize(figure);

        assertFalse(sanitized.contains("<figure"));
        assertFalse(sanitized.contains("Caption only"));
        assertTrue(sanitized.contains("Before"));
        assertTrue(sanitized.contains("After"));
    }

    // ========================================
    // Edge Cases and Utility Methods
    // ========================================

    @Test
    @DisplayName("Should handle null input")
    void testNullInput() {
        String sanitized = sanitizationService.sanitize(null);
        assertEquals("", sanitized);
    }

    @Test
    @DisplayName("Should handle empty input")
    void testEmptyInput() {
        String sanitized = sanitizationService.sanitize("");
        assertEquals("", sanitized);
    }

    @Test
    @DisplayName("Should handle whitespace-only input")
    void testWhitespaceInput() {
        String sanitized = sanitizationService.sanitize("   ");
        assertEquals("", sanitized);
    }

    @Test
    @DisplayName("Should extract plain text from HTML")
    void testExtractPlainText() {
        String html = "<p>Hello <strong>World</strong></p>";
        String plainText = sanitizationService.extractPlainText(html);

        assertEquals("Hello World", plainText);
        assertFalse(plainText.contains("<"));
        assertFalse(plainText.contains(">"));
    }

    @Test
    @DisplayName("Should detect clean HTML")
    void testIsCleanWithCleanHtml() {
        String clean = "<p>Safe content</p>";
        assertTrue(sanitizationService.isClean(clean));
    }

    @Test
    @DisplayName("Should detect malicious HTML")
    void testIsCleanWithMaliciousHtml() {
        String malicious = "<p>Content</p><script>alert('XSS')</script>";
        assertFalse(sanitizationService.isClean(malicious));
    }

    @Test
    @DisplayName("Should sanitize and extract text")
    void testSanitizeAndExtractText() {
        String html = "<p>Hello</p><script>alert('XSS')</script>";
        String result = sanitizationService.sanitizeAndExtractText(html);

        assertEquals("Hello", result);
        assertFalse(result.contains("script"));
        assertFalse(result.contains("XSS"));
    }

    // ========================================
    // Configuration Tests
    // ========================================

    @Test
    @DisplayName("Should load and provide configuration")
    void testGetConfiguration() {
        HtmlSanitizationConfigDto config = sanitizationService.getConfiguration();

        assertNotNull(config);
        assertNotNull(config.getAllowedTags());
        assertFalse(config.getAllowedTags().isEmpty());
        assertTrue(config.getAllowedTags().contains("p"));
        assertTrue(config.getAllowedTags().contains("a"));
        assertTrue(config.getAllowedTags().contains("img"));
    }

    // ========================================
    // Complex Attack Vectors
    // ========================================

    @Test
    @DisplayName("Should prevent nested XSS attacks")
    void testNestedXssAttacks() {
        String nested = "<p><script><script>alert('XSS')</script></script></p>";
        String sanitized = sanitizationService.sanitize(nested);

        assertFalse(sanitized.contains("<script"));
        assertFalse(sanitized.contains("alert"));
    }

    @Test
    @DisplayName("Should prevent encoded XSS attacks")
    void testEncodedXssAttacks() {
        String encoded = "<img src=x onerror='alert(1)'>";
        String sanitized = sanitizationService.sanitize(encoded);

        assertFalse(sanitized.contains("onerror"));
        assertFalse(sanitized.contains("alert"));
    }

    @Test
    @DisplayName("Should prevent CSS injection attacks")
    void testCssInjectionPrevention() {
        String cssInjection = "<p style='background:url(javascript:alert(1))'>Text</p>";
        String sanitized = sanitizationService.sanitize(cssInjection);

        assertFalse(sanitized.toLowerCase().contains("javascript:"));
    }

    @Test
    @DisplayName("Should preserve multiple safe elements")
    void testComplexSafeHtml() {
        String complex = "<div><h1>Title</h1><p>Paragraph with <strong>bold</strong> and " +
                        "<em>italic</em></p><ul><li>Item 1</li><li>Item 2</li></ul>" +
                        "<img src='/image.jpg' alt='Image'></div>";
        String sanitized = sanitizationService.sanitize(complex);

        assertTrue(sanitized.contains("<div"));
        assertTrue(sanitized.contains("<h1>"));
        assertTrue(sanitized.contains("<p>"));
        assertTrue(sanitized.contains("<strong>"));
        assertTrue(sanitized.contains("<em>"));
        assertTrue(sanitized.contains("<ul>"));
        assertTrue(sanitized.contains("<li>"));
        assertTrue(sanitized.contains("<img"));
        assertTrue(sanitized.contains("Title"));
        assertTrue(sanitized.contains("Item 1"));
    }

    @Test
    @DisplayName("Should handle malformed HTML gracefully")
    void testMalformedHtml() {
        String malformed = "<p>Unclosed paragraph<div>Nested incorrectly</p></div>";
        String sanitized = sanitizationService.sanitize(malformed);

        // Should not throw exception and should return something
        assertNotNull(sanitized);
        assertTrue(sanitized.contains("Unclosed paragraph"));
        assertTrue(sanitized.contains("Nested incorrectly"));
    }
}
