package com.developx.szpitale.ingest.normalize;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import java.util.List;

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

class NameNormalizerTest {

    private final NameNormalizer normalizer = new NameNormalizer();

    @Test
    void normalize_polishDiacritics_strippedToAsciiSlug() {
        String slug = normalizer.makeSlug("Łukasz Żółć");
        assertEquals("lukasz-zolc", slug);
    }

    @Test
    void normalize_slug_lowercaseSeparator() {
        String slug = normalizer.makeSlug("Jan Kowalski");
        assertEquals("jan-kowalski", slug);
    }

    @Test
    void normalize_slug_multipleSpacesToOne() {
        String slug = normalizer.makeSlug("Jan    Kowalski");
        assertEquals("jan-kowalski", slug);
    }

    @Test
    void normalize_slug_removesSpecialChars() {
        String slug = normalizer.makeSlug("Dr. Jan Kowalski Jr.");
        assertEquals("dr-jan-kowalski-jr", slug);
    }

    @ParameterizedTest
    @CsvSource({
        "'prof. dr hab. Jan Kochanowicz', 'Jan Kochanowicz'",
        "'dr Jan Nowak', 'Jan Nowak'",
        "'mgr inż. Anna Kowalska', 'Anna Kowalska'",
        "'Jan Wiśniewski', 'Jan Wiśniewski'",
        "'prof. Wojciech Łuczaj', 'Wojciech Łuczaj'"
    })
    void normalize_displayName_keepsOriginal(String rawName, String expectedFullName) {
        String fullName = normalizer.extractFullName(rawName);
        assertEquals(expectedFullName, fullName);
    }

    @ParameterizedTest
    @CsvSource({
        "'prof. dr hab. Jan Kochanowicz', 'prof.'",
        "'prof. dr hab. Jan Kochanowicz', 'dr hab.'",
        "'dr hab. n. med. Jan Wiśniewski', 'dr hab.'",
        "'dr hab. n. med. Jan Wiśniewski', 'n. med.'",
        "'mgr inż. Anna Kowalska', 'mgr inż.'"
    })
    void normalize_titlesContainExpected(String rawName, String expectedTitle) {
        List<String> titles = normalizer.extractTitles(rawName);
        assertTrue(titles.contains(expectedTitle), "Expected title '" + expectedTitle + "' not found in " + titles);
    }

    @Test
    void normalize_titlesEmptyForPlainName() {
        List<String> titles = normalizer.extractTitles("Jan Nowak");
        assertTrue(titles.isEmpty());
    }

    @Test
    void normalize_emptyTitlesWhenNoTitles() {
        List<String> titles = normalizer.extractTitles("Jan Kowalski");
        assertTrue(titles.isEmpty());
    }

    @Test
    void makePersonId_createsPersonSlug() {
        String id = normalizer.makePersonId("Jan Kowalski");
        assertEquals("person:jan-kowalski", id);
    }

    @Test
    void makePerson_idUsesSameSlugAsMakeSlug() {
        String id = normalizer.makePersonId("Łukasz Żółć");
        assertEquals("person:lukasz-zolc", id);
    }

    @Test
    void makeHospitalId_createsHospitalSlug() {
        String id = normalizer.makeHospitalId("podlaskie", "Uniwersytecki Szpital Kliniczny");
        assertEquals("hosp:podlaskie:uniwersytecki-szpital-kliniczny", id);
    }

    @Test
    void fuzzySimilarity_identicalStringsReturn1() {
        double similarity = normalizer.fuzzySimilarity("Jan Kowalski", "Jan Kowalski");
        assertEquals(1.0, similarity, 0.0);
    }

    @Test
    void fuzzySimilarity_typoReturnsHigh() {
        double similarity = normalizer.fuzzySimilarity("Jan Kowalski", "Jan Kowaski");
        assertTrue(similarity > 0.9, "Typo 'Kowalski' vs 'Kowaski' should have very high similarity: " + similarity);
    }

    @Test
    void fuzzySimilarity_differentNamesReturnLower() {
        double similarity = normalizer.fuzzySimilarity("Jan Kowalski", "Anna Nowak");
        assertTrue(similarity < 0.7, "Two completely different names should have moderate similarity: " + similarity);
    }

    @Test
    void fuzzySimilarity_handlesPolishCharacters() {
        double similarity = normalizer.fuzzySimilarity("Jan Kowalski", "Jan Kowalscy");
        assertTrue(similarity > 0.8, "Names differing only by suffix should have high similarity: " + similarity);
    }

    @Test
    void findDuplicateCandidates_returnsCandidatesAboveThreshold() {
        List<String> names = List.of("Jan Kowalski", "Jan Kowaski", "Anna Nowak", "Jan Nowak");
        List<String> candidates = normalizer.findDuplicateCandidates(names, "Jan Kowalski", 0.7);
        assertTrue(candidates.contains("Jan Kowaski"), "Should contain similar name");
    }

    @Test
    void findDuplicateCandidates_filtersTargetName() {
        List<String> names = List.of("Jan Kowalski");
        List<String> candidates = normalizer.findDuplicateCandidates(names, "Jan Kowalski", 0.7);
        assertTrue(candidates.isEmpty(), "Should not return the target name itself");
    }
}
