package com.tasteby.util;

import java.text.Normalizer;
import java.util.HashMap;
import java.util.Map;

/**
 * #348 — 한국어 자모 분해(Unicode NFD) + Sørensen-Dice bigram 유사도.
 *
 * 음절 단위 Jaccard보다 짧은 한국어 이름에 정확. 예:
 *   similarity("스타벅스 강남", "스타벅스 강남점")  ≈ 0.85+
 *   similarity("스타벅스 강남", "스타벅스 종로")    ≈ 0.55~0.85
 *   similarity("스타벅스", "맥도날드")              < 0.20
 *
 * 공백/구두점은 제거하고 소문자화한 뒤 NFD 분해.
 */
public final class HangulSimilarity {

    private HangulSimilarity() {}

    /** 공백/구두점 제거 + 소문자화 + NFD 분해(한글 음절 → 자모). */
    public static String decompose(String s) {
        if (s == null || s.isEmpty()) return "";
        String stripped = s.replaceAll("[\\s·\\-_()（）\\[\\]【】]", "").toLowerCase();
        return Normalizer.normalize(stripped, Normalizer.Form.NFD);
    }

    /**
     * Sørensen-Dice 계수 (bigram multiset 기반). 0.0~1.0.
     * 동일 문자열 → 1.0. 빈 입력 → 0.0.
     */
    public static double similarity(String a, String b) {
        String da = decompose(a);
        String db = decompose(b);
        if (da.isEmpty() || db.isEmpty()) return 0.0;
        if (da.equals(db)) return 1.0;

        // 포함 관계는 강한 신호로 1.0 처리 (기존 동작과 일관)
        if (da.contains(db) || db.contains(da)) return 1.0;

        if (da.length() < 2 || db.length() < 2) {
            return 0.0;
        }

        Map<String, Integer> bigramsA = bigrams(da);
        Map<String, Integer> bigramsB = bigrams(db);
        int common = 0;
        for (var e : bigramsA.entrySet()) {
            Integer countB = bigramsB.get(e.getKey());
            if (countB != null) {
                common += Math.min(e.getValue(), countB);
            }
        }
        int sizeA = da.length() - 1;
        int sizeB = db.length() - 1;
        return (2.0 * common) / (sizeA + sizeB);
    }

    private static Map<String, Integer> bigrams(String s) {
        Map<String, Integer> map = new HashMap<>();
        for (int i = 0; i < s.length() - 1; i++) {
            String gram = s.substring(i, i + 2);
            map.merge(gram, 1, Integer::sum);
        }
        return map;
    }
}
