package de.gecheckt.pdf.umbenenner.domain.model;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

/**
 * Unit tests for {@link SourceDocumentLocator} value object.
 * <p>
 * Verifies immutability, value semantics, and validation of the opaque document locator.
 */
class SourceDocumentLocatorTest {

    private static final String VALID_LOCATOR = "C:\\Users\\test\\documents\\file.pdf";
    private static final String VALID_LOCATOR_2 = "/home/user/documents/another.pdf";
    private static final String VALID_LOCATOR_3 = "relative/path/to/document.pdf";

    @Test
    void constructor_createsSourceDocumentLocatorWithValidValue() {
        SourceDocumentLocator locator = new SourceDocumentLocator(VALID_LOCATOR);
        assertNotNull(locator);
        assertEquals(VALID_LOCATOR, locator.value());
    }

    @Test
    void constructor_throwsNullPointerExceptionWhenValueIsNull() {
        assertThrows(NullPointerException.class, () -> new SourceDocumentLocator(null),
                "Constructor should throw NullPointerException for null value");
    }

    @Test
    void constructor_throwsIllegalArgumentExceptionWhenValueIsEmpty() {
        assertThrows(IllegalArgumentException.class, () -> new SourceDocumentLocator(""),
                "Constructor should throw IllegalArgumentException for empty value");
    }

    @Test
    void constructor_acceptsValidPaths() {
        SourceDocumentLocator locator1 = new SourceDocumentLocator(VALID_LOCATOR);
        SourceDocumentLocator locator2 = new SourceDocumentLocator(VALID_LOCATOR_2);
        SourceDocumentLocator locator3 = new SourceDocumentLocator(VALID_LOCATOR_3);

        assertEquals(VALID_LOCATOR, locator1.value());
        assertEquals(VALID_LOCATOR_2, locator2.value());
        assertEquals(VALID_LOCATOR_3, locator3.value());
    }

    @Test
    void constructor_acceptsOpaqueValue() {
        // The locator is intentionally opaque - it can contain any non-empty string
        String opaqueValue = "adapter-internal-encoding:12345:abcdef";
        SourceDocumentLocator locator = new SourceDocumentLocator(opaqueValue);
        assertEquals(opaqueValue, locator.value());
    }

    @Test
    void value_returnsTheConstructorValue() {
        SourceDocumentLocator locator = new SourceDocumentLocator(VALID_LOCATOR);
        assertEquals(VALID_LOCATOR, locator.value());
    }

    @Test
    void equals_returnsTrueForIdenticalValues() {
        SourceDocumentLocator locator1 = new SourceDocumentLocator(VALID_LOCATOR);
        SourceDocumentLocator locator2 = new SourceDocumentLocator(VALID_LOCATOR);
        assertEquals(locator1, locator2);
    }

    @Test
    void equals_returnsFalseForDifferentValues() {
        SourceDocumentLocator locator1 = new SourceDocumentLocator(VALID_LOCATOR);
        SourceDocumentLocator locator2 = new SourceDocumentLocator(VALID_LOCATOR_2);
        assertNotEquals(locator1, locator2);
    }

    @Test
    void equals_returnsFalseWhenComparedWithNull() {
        SourceDocumentLocator locator = new SourceDocumentLocator(VALID_LOCATOR);
        assertNotEquals(null, locator);
    }

    @Test
    void equals_returnsFalseWhenComparedWithDifferentType() {
        SourceDocumentLocator locator = new SourceDocumentLocator(VALID_LOCATOR);
        assertNotEquals(VALID_LOCATOR, locator);
    }

    @Test
    void hashCode_isSameForIdenticalValues() {
        SourceDocumentLocator locator1 = new SourceDocumentLocator(VALID_LOCATOR);
        SourceDocumentLocator locator2 = new SourceDocumentLocator(VALID_LOCATOR);
        assertEquals(locator1.hashCode(), locator2.hashCode());
    }

    @Test
    void hashCode_isDifferentForDifferentValues() {
        SourceDocumentLocator locator1 = new SourceDocumentLocator(VALID_LOCATOR);
        SourceDocumentLocator locator2 = new SourceDocumentLocator(VALID_LOCATOR_2);
        assertNotEquals(locator1.hashCode(), locator2.hashCode());
    }

    @Test
    void toString_containsTheValue() {
        SourceDocumentLocator locator = new SourceDocumentLocator(VALID_LOCATOR);
        assertTrue(locator.toString().contains(VALID_LOCATOR));
    }

    @Test
    void sourceDocumentLocatorCanBeUsedAsMapKey() {
        SourceDocumentLocator locator1 = new SourceDocumentLocator(VALID_LOCATOR);
        SourceDocumentLocator locator2 = new SourceDocumentLocator(VALID_LOCATOR);
        SourceDocumentLocator locator3 = new SourceDocumentLocator(VALID_LOCATOR_2);

        var map = new java.util.HashMap<SourceDocumentLocator, String>();
        map.put(locator1, "file1");
        map.put(locator2, "file2"); // Should overwrite
        map.put(locator3, "file3");

        assertEquals(2, map.size());
        assertEquals("file2", map.get(locator1));
    }

    @Test
    void sourceDocumentLocatorCanBeUsedInSets() {
        SourceDocumentLocator locator1 = new SourceDocumentLocator(VALID_LOCATOR);
        SourceDocumentLocator locator2 = new SourceDocumentLocator(VALID_LOCATOR);
        SourceDocumentLocator locator3 = new SourceDocumentLocator(VALID_LOCATOR_2);

        var set = new java.util.HashSet<SourceDocumentLocator>();
        set.add(locator1);
        set.add(locator2); // Should not add duplicate
        set.add(locator3);

        assertEquals(2, set.size());
    }

    @Test
    void constructor_acceptsWhitespaceInValue() {
        String valueWithWhitespace = "path with spaces/to/file.pdf";
        SourceDocumentLocator locator = new SourceDocumentLocator(valueWithWhitespace);
        assertEquals(valueWithWhitespace, locator.value());
    }

    @Test
    void constructor_acceptsSingleCharacterValue() {
        SourceDocumentLocator locator = new SourceDocumentLocator("a");
        assertEquals("a", locator.value());
    }
}
