package pl.polskalokalnie.moderation;

import java.text.Normalizer;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

/**
 * Globalny filtr tresci. Jedynym zrodlem slow zakazanych jest tabela forbidden_words
 * (domyslny slownik wsiewa {@link ForbiddenWordSeeder}). Kazde pole tekstowe przechodzace
 * przez {@link #validateOrThrow} jest normalizowane tak, by wykryc celowe modyfikacje:
 * rozna wielkosc liter, polskie znaki, spacje/kropki/myslniki/podkreslenia, zamiana liter na
 * cyfry (leet) oraz wielokrotne powtarzanie liter (np. "kuuurwa", "k.u_r-w4").
 */
@Service
public class TextModerationService {

    private static final Locale POLISH_LOCALE = Locale.forLanguageTag("pl-PL");

    private static final String BLOCK_MESSAGE =
            "Twoja wiadomość zawiera niedozwolone słownictwo. Usuń je i spróbuj ponownie.";

    private final ForbiddenWordRepository forbiddenWordRepository;

    public TextModerationService(ForbiddenWordRepository forbiddenWordRepository) {
        this.forbiddenWordRepository = forbiddenWordRepository;
    }

    public void validateOrThrow(String... values) {
        if (values == null) {
            return;
        }
        for (String value : values) {
            if (containsProhibitedContent(value)) {
                throw new ResponseStatusException(HttpStatus.BAD_REQUEST, BLOCK_MESSAGE);
            }
        }
    }

    public boolean containsProhibitedContent(String value) {
        if (value == null || value.isBlank()) {
            return false;
        }
        String normalized = normalize(value);
        if (normalized.isBlank()) {
            return false;
        }
        for (String banned : allNormalizedWords()) {
            if (normalized.contains(banned)) {
                return true;
            }
        }
        return false;
    }

    public List<ForbiddenWordResponse> listForbiddenWords() {
        return forbiddenWordRepository.findAll().stream()
                .sorted(Comparator.comparing(ForbiddenWord::getWord, String.CASE_INSENSITIVE_ORDER))
                .map(ForbiddenWordResponse::from)
                .toList();
    }

    public List<ForbiddenWordResponse> addForbiddenWord(String rawWord) {
        String cleaned = rawWord == null ? "" : rawWord.trim().toLowerCase(POLISH_LOCALE);
        if (cleaned.isBlank()) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Słowo nie może być puste");
        }

        String normalized = normalize(cleaned);
        if (normalized.isBlank()) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Nieprawidłowe słowo");
        }

        if (forbiddenWordRepository.existsByWordIgnoreCase(cleaned) || allNormalizedWords().contains(normalized)) {
            throw new ResponseStatusException(HttpStatus.CONFLICT, "To słowo jest już na liście zakazanych");
        }

        ForbiddenWord forbiddenWord = new ForbiddenWord();
        forbiddenWord.setWord(cleaned);
        forbiddenWordRepository.save(forbiddenWord);
        return listForbiddenWords();
    }

    public void removeForbiddenWord(Long id) {
        if (!forbiddenWordRepository.existsById(id)) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Słowo nie istnieje na liście");
        }
        forbiddenWordRepository.deleteById(id);
    }

    private Set<String> allNormalizedWords() {
        Set<String> all = new LinkedHashSet<>();
        forbiddenWordRepository.findAll().stream()
                .map(ForbiddenWord::getWord)
                .map(TextModerationService::normalize)
                .filter(word -> !word.isBlank())
                .forEach(all::add);
        return all;
    }

    private static String normalize(String value) {
        String lower = value.toLowerCase(Locale.ROOT);
        String deaccented = Normalizer.normalize(lower, Normalizer.Form.NFD)
                .replaceAll("\\p{M}+", "");

        StringBuilder lettersOnly = new StringBuilder(deaccented.length());
        for (int i = 0; i < deaccented.length(); i++) {
            char c = mapLeet(deaccented.charAt(i));
            if (Character.isLetter(c)) {
                lettersOnly.append(c);
            }
        }

        return collapseRepeatingLetters(lettersOnly.toString());
    }

    private static char mapLeet(char c) {
        return switch (c) {
            case '0' -> 'o';
            case '1' -> 'i';
            case '3' -> 'e';
            case '4' -> 'a';
            case '5' -> 's';
            case '7' -> 't';
            default -> c;
        };
    }

    private static String collapseRepeatingLetters(String value) {
        if (value.isEmpty()) {
            return value;
        }

        StringBuilder collapsed = new StringBuilder(value.length());
        char prev = value.charAt(0);
        collapsed.append(prev);
        for (int i = 1; i < value.length(); i++) {
            char current = value.charAt(i);
            if (current != prev) {
                collapsed.append(current);
                prev = current;
            }
        }
        return collapsed.toString();
    }
}
