package de.effigenix.infrastructure.shared;

import de.effigenix.shared.common.Country;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class InMemoryCountryRepositoryTest {

    private InMemoryCountryRepository repository;

    @BeforeEach
    void setUp() {
        repository = new InMemoryCountryRepository();
    }

    @Test
    void findAllShouldReturnAllCountries() {
        var all = repository.findAll();
        assertThat(all).hasSizeGreaterThanOrEqualTo(200);
    }

    @Test
    void findAllShouldHaveDachFirst() {
        var all = repository.findAll();
        assertThat(all.get(0).code()).isEqualTo("DE");
        assertThat(all.get(1).code()).isEqualTo("AT");
        assertThat(all.get(2).code()).isEqualTo("CH");
    }

    @Test
    void searchByNameShouldFindGermany() {
        var results = repository.search("deutsch");
        assertThat(results).extracting(Country::code).contains("DE");
    }

    @Test
    void searchByCodeShouldFindGermany() {
        var results = repository.search("DE");
        assertThat(results).extracting(Country::code).contains("DE");
    }

    @Test
    void searchShouldPrioritizeDach() {
        var results = repository.search("sch");
        // Schweiz (CH) should appear before non-DACH results like Tadschikistan
        var codes = results.stream().map(Country::code).toList();
        if (codes.contains("CH") && codes.contains("TJ")) {
            assertThat(codes.indexOf("CH")).isLessThan(codes.indexOf("TJ"));
        }
    }

    @Test
    void searchWithNoMatchShouldReturnEmptyList() {
        var results = repository.search("xyz123");
        assertThat(results).isEmpty();
    }

    @Test
    void searchWithNullShouldReturnAll() {
        var results = repository.search(null);
        assertThat(results).hasSizeGreaterThanOrEqualTo(200);
    }

    @Test
    void searchWithEmptyStringShouldReturnAll() {
        var results = repository.search("");
        assertThat(results).hasSizeGreaterThanOrEqualTo(200);
    }

    @Test
    void findByCodeShouldReturnCountry() {
        var result = repository.findByCode("DE");
        assertThat(result).isPresent();
        assertThat(result.get().name()).isEqualTo("Deutschland");
    }

    @Test
    void findByCodeShouldReturnEmptyForUnknown() {
        var result = repository.findByCode("XX");
        assertThat(result).isEmpty();
    }

    @Test
    void findByCodeWithNullShouldReturnEmpty() {
        var result = repository.findByCode(null);
        assertThat(result).isEmpty();
    }

    @Test
    void searchShouldBeCaseInsensitive() {
        var results = repository.search("DEUTSCH");
        assertThat(results).extracting(Country::code).contains("DE");
    }

    @Test
    void searchByLowercaseCodeShouldMatch() {
        var results = repository.search("de");
        assertThat(results).extracting(Country::code).contains("DE");
    }

    @Test
    void searchWithWhitespaceOnlyShouldReturnAll() {
        var results = repository.search("   ");
        assertThat(results).hasSizeGreaterThanOrEqualTo(200);
    }

    @Test
    void findAllShouldSortNonDachAlphabetically() {
        var all = repository.findAll();
        // Skip DACH (first 3), check alphabetical order of the rest
        var nonDach = all.subList(3, all.size());
        for (int i = 1; i < nonDach.size(); i++) {
            assertThat(nonDach.get(i).name().compareTo(nonDach.get(i - 1).name()))
                    .as("'%s' should come after '%s'", nonDach.get(i).name(), nonDach.get(i - 1).name())
                    .isGreaterThanOrEqualTo(0);
        }
    }

    @Test
    void findAllShouldReturnImmutableSnapshot() {
        var first = repository.findAll();
        var second = repository.findAll();
        assertThat(first).isEqualTo(second);
        assertThat(first).hasSize(second.size());
    }

    @Test
    void searchShouldMatchPartialName() {
        var results = repository.search("Frank");
        assertThat(results).extracting(Country::code).contains("FR");
    }

    @Test
    void searchDachPrioritizationInResults() {
        // "land" matches Deutschland (DE) and other countries like Island, Irland, etc.
        var results = repository.search("land");
        var codes = results.stream().map(Country::code).toList();
        assertThat(codes).isNotEmpty();
        if (codes.contains("DE")) {
            // DE should be first since it's DACH
            assertThat(codes.get(0)).isEqualTo("DE");
        }
    }
}
