package com.tiedup.remake.util;

import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;

/**
 * Phonetic transformation system for gagged speech.
 * Maps original phonemes to muffled equivalents based on gag material.
 */
public class PhoneticMapper {

    private static final Random RANDOM = new Random();

    // Phonetic categories
    private static final Set<Character> VOWELS = Set.of(
        'a',
        'e',
        'i',
        'o',
        'u',
        'y'
    );
    private static final Set<Character> PLOSIVES = Set.of(
        'b',
        'd',
        'g',
        'k',
        'p',
        't'
    );
    private static final Set<Character> NASALS = Set.of('m', 'n');
    private static final Set<Character> FRICATIVES = Set.of(
        'f',
        'h',
        's',
        'v',
        'z'
    );
    private static final Set<Character> LIQUIDS = Set.of('l', 'r');

    // Material-specific phoneme mappings
    private static final Map<
        GagMaterial,
        Map<Character, String[]>
    > CONSONANT_MAPS = new EnumMap<>(GagMaterial.class);
    private static final Map<GagMaterial, Map<Character, String[]>> VOWEL_MAPS =
        new EnumMap<>(GagMaterial.class);

    static {
        initializeClothMappings();
        initializeBallMappings();
        initializeTapeMappings();
        initializeStuffedMappings();
        initializePanelMappings();
        initializeLatexMappings();
        initializeRingMappings();
        initializeBiteMappings();
        initializeSpongeMappings();
        initializeBaguetteMappings();
    }

    private static void initializeClothMappings() {
        Map<Character, String[]> consonants = new HashMap<>();
        consonants.put('b', new String[] { "m", "mph" });
        consonants.put('c', new String[] { "h", "kh" });
        consonants.put('d', new String[] { "n", "nd" });
        consonants.put('f', new String[] { "f", "ph" });
        consonants.put('g', new String[] { "ng", "gh" });
        consonants.put('h', new String[] { "h", "hh" });
        consonants.put('j', new String[] { "zh", "jh" });
        consonants.put('k', new String[] { "kh", "gh" });
        consonants.put('l', new String[] { "l", "hl" });
        consonants.put('m', new String[] { "m", "mm" });
        consonants.put('n', new String[] { "n", "nn" });
        consonants.put('p', new String[] { "mph", "m" });
        consonants.put('q', new String[] { "kh", "gh" });
        consonants.put('r', new String[] { "r", "hr" });
        consonants.put('s', new String[] { "s", "sh" });
        consonants.put('t', new String[] { "th", "n" });
        consonants.put('v', new String[] { "f", "vh" });
        consonants.put('w', new String[] { "wh", "u" });
        consonants.put('x', new String[] { "ks", "kh" });
        consonants.put('z', new String[] { "z", "s" });
        CONSONANT_MAPS.put(GagMaterial.CLOTH, consonants);

        Map<Character, String[]> vowels = new HashMap<>();
        vowels.put('a', new String[] { "ah", "a" });
        vowels.put('e', new String[] { "eh", "e" });
        vowels.put('i', new String[] { "ih", "e" });
        vowels.put('o', new String[] { "oh", "o" });
        vowels.put('u', new String[] { "uh", "u" });
        vowels.put('y', new String[] { "ih", "e" });
        VOWEL_MAPS.put(GagMaterial.CLOTH, vowels);
    }

    private static void initializeBallMappings() {
        Map<Character, String[]> consonants = new HashMap<>();
        // Ball gag forces mouth open around ball - tongue and lips blocked
        consonants.put('b', new String[] { "m", "mm" });
        consonants.put('c', new String[] { "g", "kh" });
        consonants.put('d', new String[] { "n", "nn" });
        consonants.put('f', new String[] { "h", "hh" });
        consonants.put('g', new String[] { "ng", "g" });
        consonants.put('h', new String[] { "h", "hh" });
        consonants.put('j', new String[] { "g", "ng" });
        consonants.put('k', new String[] { "g", "gh" });
        consonants.put('l', new String[] { "u", "l" });
        consonants.put('m', new String[] { "m", "mm" });
        consonants.put('n', new String[] { "n", "nn" });
        consonants.put('p', new String[] { "m", "mm" });
        consonants.put('q', new String[] { "g", "gh" });
        consonants.put('r', new String[] { "u", "r" });
        consonants.put('s', new String[] { "h", "s" });
        consonants.put('t', new String[] { "n", "nn" });
        consonants.put('v', new String[] { "h", "f" });
        consonants.put('w', new String[] { "u", "w" });
        consonants.put('x', new String[] { "g", "ks" });
        consonants.put('z', new String[] { "s", "z" });
        CONSONANT_MAPS.put(GagMaterial.BALL, consonants);

        Map<Character, String[]> vowels = new HashMap<>();
        // Ball forces all vowels toward "oo/uu" (rounded)
        vowels.put('a', new String[] { "a", "o" });
        vowels.put('e', new String[] { "u", "o" });
        vowels.put('i', new String[] { "u", "i" });
        vowels.put('o', new String[] { "o", "oo" });
        vowels.put('u', new String[] { "u", "uu" });
        vowels.put('y', new String[] { "u", "i" });
        VOWEL_MAPS.put(GagMaterial.BALL, vowels);
    }

    private static void initializeTapeMappings() {
        Map<Character, String[]> consonants = new HashMap<>();
        // Tape seals mouth almost completely - only nasal sounds
        consonants.put('b', new String[] { "m", "mm" });
        consonants.put('c', new String[] { "n", "m" });
        consonants.put('d', new String[] { "n", "nn" });
        consonants.put('f', new String[] { "m", "hm" });
        consonants.put('g', new String[] { "n", "ng" });
        consonants.put('h', new String[] { "m", "hm" });
        consonants.put('j', new String[] { "n", "m" });
        consonants.put('k', new String[] { "n", "m" });
        consonants.put('l', new String[] { "n", "m" });
        consonants.put('m', new String[] { "m", "mm" });
        consonants.put('n', new String[] { "n", "nn" });
        consonants.put('p', new String[] { "m", "mm" });
        consonants.put('q', new String[] { "n", "m" });
        consonants.put('r', new String[] { "n", "m" });
        consonants.put('s', new String[] { "m", "s" });
        consonants.put('t', new String[] { "n", "nn" });
        consonants.put('v', new String[] { "m", "f" });
        consonants.put('w', new String[] { "m", "u" });
        consonants.put('x', new String[] { "n", "m" });
        consonants.put('z', new String[] { "n", "m" });
        CONSONANT_MAPS.put(GagMaterial.TAPE, consonants);

        Map<Character, String[]> vowels = new HashMap<>();
        // Tape muffles all vowels to near silence
        vowels.put('a', new String[] { "m", "mm" });
        vowels.put('e', new String[] { "n", "m" });
        vowels.put('i', new String[] { "n", "m" });
        vowels.put('o', new String[] { "m", "mm" });
        vowels.put('u', new String[] { "m", "u" });
        vowels.put('y', new String[] { "n", "m" });
        VOWEL_MAPS.put(GagMaterial.TAPE, vowels);
    }

    private static void initializeStuffedMappings() {
        Map<Character, String[]> consonants = new HashMap<>();
        // Stuffed gag - nearly silent
        for (char c = 'a'; c <= 'z'; c++) {
            if (!VOWELS.contains(c)) {
                consonants.put(c, new String[] { "mm", "m", "" });
            }
        }
        CONSONANT_MAPS.put(GagMaterial.STUFFED, consonants);

        Map<Character, String[]> vowels = new HashMap<>();
        for (char c : VOWELS) {
            vowels.put(c, new String[] { "mm", "m", "" });
        }
        VOWEL_MAPS.put(GagMaterial.STUFFED, vowels);
    }

    private static void initializePanelMappings() {
        // Panel gag - similar to tape but slightly more sound
        Map<Character, String[]> consonants = new HashMap<>();
        consonants.put('b', new String[] { "m", "mph" });
        consonants.put('c', new String[] { "n", "m" });
        consonants.put('d', new String[] { "n", "nd" });
        consonants.put('f', new String[] { "m", "f" });
        consonants.put('g', new String[] { "n", "ng" });
        consonants.put('h', new String[] { "hm", "m" });
        consonants.put('j', new String[] { "n", "m" });
        consonants.put('k', new String[] { "n", "m" });
        consonants.put('l', new String[] { "n", "m" });
        consonants.put('m', new String[] { "m", "mm" });
        consonants.put('n', new String[] { "n", "nn" });
        consonants.put('p', new String[] { "m", "mph" });
        consonants.put('q', new String[] { "n", "m" });
        consonants.put('r', new String[] { "n", "m" });
        consonants.put('s', new String[] { "s", "m" });
        consonants.put('t', new String[] { "n", "m" });
        consonants.put('v', new String[] { "m", "f" });
        consonants.put('w', new String[] { "m", "u" });
        consonants.put('x', new String[] { "n", "m" });
        consonants.put('z', new String[] { "n", "m" });
        CONSONANT_MAPS.put(GagMaterial.PANEL, consonants);

        Map<Character, String[]> vowels = new HashMap<>();
        vowels.put('a', new String[] { "m", "ah" });
        vowels.put('e', new String[] { "n", "m" });
        vowels.put('i', new String[] { "n", "m" });
        vowels.put('o', new String[] { "m", "oh" });
        vowels.put('u', new String[] { "m", "u" });
        vowels.put('y', new String[] { "n", "m" });
        VOWEL_MAPS.put(GagMaterial.PANEL, vowels);
    }

    private static void initializeLatexMappings() {
        Map<Character, String[]> consonants = new HashMap<>();
        // Latex - tight seal, rubber sounds
        consonants.put('b', new String[] { "m", "mm" });
        consonants.put('c', new String[] { "n", "m" });
        consonants.put('d', new String[] { "n", "nn" });
        consonants.put('f', new String[] { "h", "f" });
        consonants.put('g', new String[] { "ng", "m" });
        consonants.put('h', new String[] { "h", "hh" });
        consonants.put('j', new String[] { "n", "m" });
        consonants.put('k', new String[] { "n", "m" });
        consonants.put('l', new String[] { "n", "m" });
        consonants.put('m', new String[] { "m", "mm" });
        consonants.put('n', new String[] { "n", "nn" });
        consonants.put('p', new String[] { "m", "mm" });
        consonants.put('q', new String[] { "n", "m" });
        consonants.put('r', new String[] { "n", "m" });
        consonants.put('s', new String[] { "s", "h" });
        consonants.put('t', new String[] { "n", "nn" });
        consonants.put('v', new String[] { "f", "m" });
        consonants.put('w', new String[] { "m", "u" });
        consonants.put('x', new String[] { "n", "m" });
        consonants.put('z', new String[] { "s", "m" });
        CONSONANT_MAPS.put(GagMaterial.LATEX, consonants);

        Map<Character, String[]> vowels = new HashMap<>();
        vowels.put('a', new String[] { "u", "a" });
        vowels.put('e', new String[] { "u", "e" });
        vowels.put('i', new String[] { "u", "i" });
        vowels.put('o', new String[] { "u", "o" });
        vowels.put('u', new String[] { "u", "uu" });
        vowels.put('y', new String[] { "u", "i" });
        VOWEL_MAPS.put(GagMaterial.LATEX, vowels);
    }

    private static void initializeRingMappings() {
        Map<Character, String[]> consonants = new HashMap<>();
        // Ring gag - mouth forced open, tongue partially free
        consonants.put('b', new String[] { "b", "bh" });
        consonants.put('c', new String[] { "k", "kh" });
        consonants.put('d', new String[] { "d", "dh" });
        consonants.put('f', new String[] { "f", "fh" });
        consonants.put('g', new String[] { "g", "gh" });
        consonants.put('h', new String[] { "h", "ah" });
        consonants.put('j', new String[] { "j", "zh" });
        consonants.put('k', new String[] { "k", "kh" });
        consonants.put('l', new String[] { "l", "lh" });
        consonants.put('m', new String[] { "m", "mh" });
        consonants.put('n', new String[] { "n", "nh" });
        consonants.put('p', new String[] { "p", "ph" });
        consonants.put('q', new String[] { "k", "kh" });
        consonants.put('r', new String[] { "r", "rh" });
        consonants.put('s', new String[] { "s", "sh" });
        consonants.put('t', new String[] { "t", "th" });
        consonants.put('v', new String[] { "v", "vh" });
        consonants.put('w', new String[] { "w", "wh" });
        consonants.put('x', new String[] { "ks", "kh" });
        consonants.put('z', new String[] { "z", "zh" });
        CONSONANT_MAPS.put(GagMaterial.RING, consonants);

        Map<Character, String[]> vowels = new HashMap<>();
        // Ring forces mouth open - vowels become "a/ah"
        vowels.put('a', new String[] { "a", "ah" });
        vowels.put('e', new String[] { "eh", "a" });
        vowels.put('i', new String[] { "ih", "a" });
        vowels.put('o', new String[] { "oh", "a" });
        vowels.put('u', new String[] { "uh", "a" });
        vowels.put('y', new String[] { "ih", "a" });
        VOWEL_MAPS.put(GagMaterial.RING, vowels);
    }

    private static void initializeBiteMappings() {
        Map<Character, String[]> consonants = new HashMap<>();
        // Bite gag - teeth clenched on bar
        consonants.put('b', new String[] { "bh", "ph" });
        consonants.put('c', new String[] { "kh", "gh" });
        consonants.put('d', new String[] { "dh", "th" });
        consonants.put('f', new String[] { "fh", "f" });
        consonants.put('g', new String[] { "gh", "ng" });
        consonants.put('h', new String[] { "h", "hh" });
        consonants.put('j', new String[] { "jh", "zh" });
        consonants.put('k', new String[] { "kh", "gh" });
        consonants.put('l', new String[] { "lh", "hl" });
        consonants.put('m', new String[] { "m", "mh" });
        consonants.put('n', new String[] { "n", "nh" });
        consonants.put('p', new String[] { "ph", "bh" });
        consonants.put('q', new String[] { "kh", "gh" });
        consonants.put('r', new String[] { "rh", "hr" });
        consonants.put('s', new String[] { "sh", "s" });
        consonants.put('t', new String[] { "th", "dh" });
        consonants.put('v', new String[] { "vh", "fh" });
        consonants.put('w', new String[] { "wh", "uh" });
        consonants.put('x', new String[] { "ksh", "kh" });
        consonants.put('z', new String[] { "zh", "sh" });
        CONSONANT_MAPS.put(GagMaterial.BITE, consonants);

        Map<Character, String[]> vowels = new HashMap<>();
        vowels.put('a', new String[] { "eh", "ah" });
        vowels.put('e', new String[] { "eh", "e" });
        vowels.put('i', new String[] { "ih", "eh" });
        vowels.put('o', new String[] { "oh", "eh" });
        vowels.put('u', new String[] { "uh", "eh" });
        vowels.put('y', new String[] { "ih", "eh" });
        VOWEL_MAPS.put(GagMaterial.BITE, vowels);
    }

    private static void initializeSpongeMappings() {
        // Sponge - absorbs almost all sound
        Map<Character, String[]> consonants = new HashMap<>();
        for (char c = 'a'; c <= 'z'; c++) {
            if (!VOWELS.contains(c)) {
                consonants.put(c, new String[] { "mm", "" });
            }
        }
        CONSONANT_MAPS.put(GagMaterial.SPONGE, consonants);

        Map<Character, String[]> vowels = new HashMap<>();
        for (char c : VOWELS) {
            vowels.put(c, new String[] { "mm", "" });
        }
        VOWEL_MAPS.put(GagMaterial.SPONGE, vowels);
    }

    private static void initializeBaguetteMappings() {
        Map<Character, String[]> consonants = new HashMap<>();
        // Baguette - comedic, food-blocked
        consonants.put('b', new String[] { "bm", "mm" });
        consonants.put('c', new String[] { "km", "gm" });
        consonants.put('d', new String[] { "dm", "nm" });
        consonants.put('f', new String[] { "fm", "hm" });
        consonants.put('g', new String[] { "gm", "ngm" });
        consonants.put('h', new String[] { "hm", "h" });
        consonants.put('j', new String[] { "jm", "zhm" });
        consonants.put('k', new String[] { "km", "gm" });
        consonants.put('l', new String[] { "lm", "mm" });
        consonants.put('m', new String[] { "mm", "m" });
        consonants.put('n', new String[] { "nm", "n" });
        consonants.put('p', new String[] { "pm", "mm" });
        consonants.put('q', new String[] { "km", "gm" });
        consonants.put('r', new String[] { "rm", "mm" });
        consonants.put('s', new String[] { "sm", "shm" });
        consonants.put('t', new String[] { "tm", "nm" });
        consonants.put('v', new String[] { "vm", "fm" });
        consonants.put('w', new String[] { "wm", "um" });
        consonants.put('x', new String[] { "ksm", "km" });
        consonants.put('z', new String[] { "zm", "sm" });
        CONSONANT_MAPS.put(GagMaterial.BAGUETTE, consonants);

        Map<Character, String[]> vowels = new HashMap<>();
        vowels.put('a', new String[] { "am", "om" });
        vowels.put('e', new String[] { "em", "um" });
        vowels.put('i', new String[] { "im", "um" });
        vowels.put('o', new String[] { "om", "o" });
        vowels.put('u', new String[] { "um", "u" });
        vowels.put('y', new String[] { "im", "um" });
        VOWEL_MAPS.put(GagMaterial.BAGUETTE, vowels);
    }

    /**
     * Map a single phoneme to its muffled equivalent.
     *
     * @param c            The original character
     * @param material     The gag material
     * @param bleedChance  Chance (0-1) that the original sound passes through
     * @return The muffled phoneme
     */
    public static String mapPhoneme(
        char c,
        GagMaterial material,
        float bleedChance
    ) {
        char lower = Character.toLowerCase(c);

        // Non-alphabetic characters pass through
        if (!Character.isLetter(c)) {
            return String.valueOf(c);
        }

        // Bleed-through check: original sound passes
        if (RANDOM.nextFloat() < bleedChance) {
            return String.valueOf(c);
        }

        // Get appropriate map
        Map<Character, String[]> map = isVowel(lower)
            ? VOWEL_MAPS.get(material)
            : CONSONANT_MAPS.get(material);

        if (map == null) {
            return String.valueOf(c);
        }

        String[] options = map.get(lower);
        if (options == null || options.length == 0) {
            // Default fallback
            return isVowel(lower) ? "mm" : "nn";
        }

        // Pick a random option
        String result = options[RANDOM.nextInt(options.length)];

        // Preserve case for first character
        if (Character.isUpperCase(c) && !result.isEmpty()) {
            return (
                Character.toUpperCase(result.charAt(0)) + result.substring(1)
            );
        }

        return result;
    }

    /**
     * Check if a character is a vowel.
     */
    public static boolean isVowel(char c) {
        return VOWELS.contains(Character.toLowerCase(c));
    }

    /**
     * Check if a character is a plosive consonant.
     */
    public static boolean isPlosive(char c) {
        return PLOSIVES.contains(Character.toLowerCase(c));
    }

    /**
     * Check if a character is a nasal consonant.
     */
    public static boolean isNasal(char c) {
        return NASALS.contains(Character.toLowerCase(c));
    }

    /**
     * Check if a character is a fricative consonant.
     */
    public static boolean isFricative(char c) {
        return FRICATIVES.contains(Character.toLowerCase(c));
    }

    /**
     * Check if a character is a liquid consonant.
     */
    public static boolean isLiquid(char c) {
        return LIQUIDS.contains(Character.toLowerCase(c));
    }

    /**
     * Check if a character can potentially bleed through for a given material.
     * Nasals have higher bleed-through, plosives have lower.
     */
    public static float getBleedModifier(char c, GagMaterial material) {
        char lower = Character.toLowerCase(c);

        // Nasals almost always pass through
        if (isNasal(lower)) {
            return 2.0f;
        }

        // Plosives are harder to pronounce with most gags
        if (isPlosive(lower)) {
            return material == GagMaterial.RING ? 1.5f : 0.3f;
        }

        // Fricatives depend on whether air can escape
        if (isFricative(lower)) {
            return (
                    material == GagMaterial.TAPE ||
                    material == GagMaterial.STUFFED
                )
                ? 0.1f
                : 0.8f;
        }

        // Liquids depend on tongue freedom
        if (isLiquid(lower)) {
            return (
                    material == GagMaterial.RING || material == GagMaterial.BITE
                )
                ? 1.2f
                : 0.2f;
        }

        return 1.0f;
    }
}
