import os
import re
import sys

sys.stdout.reconfigure(encoding='utf-8')

# --- Helper Functions for Japanese and Conjugations ---

def is_kanji(c):
    return '\u4e00' <= c <= '\u9fff'

def align(kanji_str, kana_str):
    memo = {}
    def search(ki, ai):
        state = (ki, ai)
        if state in memo:
            return memo[state]
        if ki == len(kanji_str) and ai == len(kana_str):
            return []
        if ki == len(kanji_str) or ai == len(kana_str):
            return None
        
        kc = kanji_str[ki]
        if not is_kanji(kc):
            if kc == kana_str[ai]:
                res = search(ki + 1, ai + 1)
                if res is not None:
                    memo[state] = [('kana', kc, kc)] + res
                    return memo[state]
            memo[state] = None
            return None
        
        # kc is kanji
        for l in range(1, len(kana_str) - ai + 1):
            sub_kana = kana_str[ai : ai + l]
            res = search(ki + 1, ai + l)
            if res is not None:
                memo[state] = [('kanji', kc, sub_kana)] + res
                return memo[state]
        
        memo[state] = None
        return None

    return search(0, 0)

def make_split_regex(chars):
    parts = []
    for c in chars:
        parts.append(re.escape(c))
    return r'(?:<\d+>|\s)*'.join(parts)

def get_godan_ending_regex(last_char):
    mapping = {
        'う': {'a': 'わ', 'i': 'い', 'u': 'う', 'e': 'え', 'o': 'お', 't': 'っ'},
        'く': {'a': 'か', 'i': 'き', 'u': 'く', 'e': 'け', 'o': 'こ', 't': 'い'},
        'ぐ': {'a': 'が', 'i': 'ぎ', 'u': 'ぐ', 'e': 'げ', 'o': 'ご', 't': 'い'},
        'す': {'a': 'さ', 'i': 'し', 'u': 'す', 'e': 'せ', 'o': 'そ', 't': 'し'},
        'つ': {'a': 'た', 'i': 'ち', 'u': 'つ', 'e': 'て', 'o': 'と', 't': 'っ'},
        'ぬ': {'a': 'な', 'i': 'に', 'u': 'ぬ', 'e': 'ね', 'o': 'の', 't': 'ん'},
        'ぶ': {'a': 'ば', 'i': 'び', 'u': 'ぶ', 'e': 'べ', 'o': 'ぼ', 't': 'ん'},
        'む': {'a': 'ま', 'i': 'み', 'u': 'む', 'e': 'め', 'o': 'も', 't': 'ん'},
        'る': {'a': 'ら', 'i': 'り', 'u': 'る', 'e': 'れ', 'o': 'ろ', 't': 'っ'},
    }
    
    chars = mapping.get(last_char)
    if not chars:
        return ""
    
    p_a = f"{chars['a']}(?:ない|なかった|ず|ぬ|れる|られる|せる|させる|れ|ら)?"
    p_i = f"{chars['i']}(?:ます|ました|ません|ましょう|たい|たかった|ながら|やすい|にくい|つつ)?"
    p_u = f"{chars['u']}"
    p_e = f"{chars['e']}(?:ば|る|れば)?"
    p_o = f"{chars['o']}う"
    
    te_char = 'で' if last_char in ['ぐ', 'ぬ', 'ぶ', 'む'] else 'て'
    ta_char = 'だ' if last_char in ['ぐ', 'ぬ', 'ぶ', 'む'] else 'た'
    
    p_t = f"{chars['t']}(?:{te_char}|{ta_char}(?:ら|り)?)"
    
    pattern = f"(?:{p_a}|{p_i}|{p_u}|{p_e}|{p_o}|{p_t})"
    return pattern

def get_ichidan_ending_regex():
    p_ru = "る"
    p_re = "れ(?:ば)?"
    p_ro = "[ろよ]"
    p_rare = "(?:られ(?:る)?|させ(?:る)?)"
    p_yo = "よう"
    p_stem = "(?:ない|なかった|ず|ぬ|ます|ました|ません|ましょう|たい|たかった|て|た(?:ら|り)?)"
    
    pattern = f"(?:{p_ru}|{p_re}|{p_ro}|{p_rare}|{p_yo}|{p_stem})?"
    return pattern

def split_tol(p):
    def repl(m):
        return make_split_regex(m.group(0))
    return re.sub(r'[\u3040-\u309f\u30a0-\u30ff\u4e00-\u9fff]+', repl, p)

# --- Alignment and Variant Generation ---

def build_verb_regex(kanji, kana):
    # Align kanji and kana
    alignment = align(kanji, kana)
    if not alignment:
        # Fallback to simple split tolerant of kanji and kana
        return f"(?:{make_split_regex(kanji)}|{make_split_regex(kana)})"
    
def is_ichidan_v2(kanji_str, kana_str):
    if not kana_str.endswith('る'):
        return False
        
    kanji_indices = [i for i, c in enumerate(kanji_str) if '\u4e00' <= c <= '\u9fff']
    if len(kanji_indices) >= 2:
        v2_candidate = kanji_str[kanji_indices[1]:]
    elif len(kanji_indices) == 1:
        v2_candidate = kanji_str[kanji_indices[0]+1:]
    else:
        v2_candidate = kanji_str
        
    if not v2_candidate:
        v2_candidate = kanji_str
        
    v2_candidate = re.sub(r'[^a-zA-Z0-9\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff]', '', v2_candidate)
    
    try:
        from jamdict import Jamdict
        j = Jamdict()
        res = j.lookup(v2_candidate)
        for entry in res.entries:
            for sense in entry.senses:
                for pos in sense.pos:
                    if 'ichidan' in pos.lower():
                        return True
                    if 'godan' in pos.lower():
                        return False
    except Exception as e:
        pass
        
    if len(kana_str) >= 2:
        char_before_ru = kana_str[-2]
        if char_before_ru in 'いきしちにひみりゐえけせてねへめれゑ':
            return True
            
    return False

def build_verb_regex(kanji, kana):
    # Align kanji and kana
    alignment = align(kanji, kana)
    if not alignment:
        # Fallback to simple split tolerant of kanji and kana
        return f"(?:{make_split_regex(kanji)}|{make_split_regex(kana)})"
    
    # Identify type of V2 ending
    last_char = kana[-1]
    is_ichidan = is_ichidan_v2(kanji, kana)
    is_godan = not is_ichidan
    
    # Reconstruct stem pattern
    # Stem is everything except the last character of the alignment
    stem_parts_kj = []
    stem_parts_kn = []
    
    for i in range(len(alignment) - 1):
        atype, kj_char, kn_chars = alignment[i]
        if atype == 'kanji':
            # Allow either the kanji character or its reading
            stem_parts_kj.append(f"(?:{re.escape(kj_char)}|{make_split_regex(kn_chars)})")
            stem_parts_kn.append(make_split_regex(kn_chars))
        else:
            stem_parts_kj.append(re.escape(kj_char))
            stem_parts_kn.append(re.escape(kn_chars))
            
    stem_kj_pat = r'(?:<\d+>|\s)*'.join(stem_parts_kj)
    stem_kn_pat = r'(?:<\d+>|\s)*'.join(stem_parts_kn)
    
    # End pattern
    if is_godan:
        end_pat = get_godan_ending_regex(last_char)
    else:
        end_pat = get_ichidan_ending_regex()
        
    end_pat_tol = split_tol(end_pat)
    
    # Combined pattern
    final_pat = f"(?:{stem_kj_pat}|{stem_kn_pat})(?:<\\d+>|\\s)*{end_pat_tol}"
    return final_pat

def build_noun_regex(noun_kanji, noun_kana):
    # For nouns, they don't conjugate, so we just allow spelling variations of V1 and V2
    alignment = align(noun_kanji, noun_kana)
    if not alignment:
        return f"(?:{make_split_regex(noun_kanji)}|{make_split_regex(noun_kana)})"
    
    parts_kj = []
    parts_kn = []
    for atype, kj_char, kn_chars in alignment:
        if atype == 'kanji':
            parts_kj.append(f"(?:{re.escape(kj_char)}|{make_split_regex(kn_chars)})")
            parts_kn.append(make_split_regex(kn_chars))
        else:
            parts_kj.append(re.escape(kj_char))
            parts_kn.append(re.escape(kn_chars))
            
    kj_pat = r'(?:<\d+>|\s)*'.join(parts_kj)
    kn_pat = r'(?:<\d+>|\s)*'.join(parts_kn)
    return f"(?:{kj_pat}|{kn_pat})"

# --- Bracket parsing ---

def extract_balanced_bracket(s, start_idx):
    start_char = s[start_idx]
    if start_char == '【':
        end_chars = ['】', ']']
    elif start_char == '[':
        end_chars = [']', '】']
    elif start_char == '「':
        end_chars = ['」', '】', ']']
    elif start_char == '〔':
        end_chars = ['〕', ']', '】']
    else:
        return None, start_idx
        
    depth = 1
    j = start_idx + 1
    while j < len(s) and depth > 0:
        if s[j] == start_char:
            depth += 1
        elif s[j] in end_chars:
            depth -= 1
        j += 1
    return s[start_idx:j], j

# --- Contraction Expansion for Headwords ---

def expand_headword_brackets(kanji_bracket):
    # E.g. "出会す・—交す・—喰わす/出っ会す・—交す・—喰わす"
    # E.g. "取り損なう・取り損ねる/捕り—/採り—/獲り—/撮り—/録り—"
    
    # Split by / to get the major variant sublists
    sublists = [sub.strip() for sub in kanji_bracket.split('/') if sub.strip()]
    if not sublists:
        return kanji_bracket
        
    first_sublist_parts = [p.strip() for p in sublists[0].split('・') if p.strip()]
    absolute_base = first_sublist_parts[0]
    
    # Absolute base prefix/suffix split (using second kanji)
    kanji_indices = [i for i, c in enumerate(absolute_base) if '\u4e00' <= c <= '\u9fff']
    if len(kanji_indices) >= 2:
        split_idx = kanji_indices[1]
    else:
        split_idx = len(absolute_base) // 2
    abs_prefix = absolute_base[:split_idx]
    abs_suffix = absolute_base[split_idx:]
    
    expanded_sublists = []
    
    for sub in sublists:
        parts = [p.strip() for p in sub.split('・') if p.strip()]
        if not parts:
            continue
            
        # Determine local base split
        local_base = parts[0]
        has_dash = any(d in local_base for d in ['—', '―', '–', '-', '〜', '～'])
        
        if not has_dash:
            # Use local base split
            k_indices = [i for i, c in enumerate(local_base) if '\u4e00' <= c <= '\u9fff']
            if len(k_indices) >= 2:
                s_idx = k_indices[1]
            else:
                s_idx = len(local_base) // 2
            local_prefix = local_base[:s_idx]
            local_suffix = local_base[s_idx:]
        else:
            # Fallback to absolute base split
            local_prefix = abs_prefix
            local_suffix = abs_suffix
            
        expanded_parts = []
        for p in parts:
            is_prefix_dash = p.startswith('—') or p.startswith('―') or p.startswith('–') or p.startswith('-') or p.startswith('〜') or p.startswith('～')
            is_suffix_dash = p.endswith('—') or p.endswith('―') or p.endswith('–') or p.endswith('-') or p.endswith('〜') or p.endswith('～')
            p_clean = re.sub(r'[—―–\-〜～]', '', p)
            
            if is_prefix_dash:
                # Replaces prefix
                expanded_parts.append(local_prefix + p_clean)
            elif is_suffix_dash:
                # Replaces suffix
                expanded_parts.append(p_clean + local_suffix)
            else:
                expanded_parts.append(p)
                
        expanded_sublists.append("・".join(expanded_parts))
        
    return "/".join(expanded_sublists)

# --- Header correction ---

def fix_header(line):
    s = line.strip()
    if not s.startswith('**'):
        return None
    
    if s.startswith('**名詞') or s.startswith('**(あちこち') or s.startswith('**相撲用語'):
        return None
        
    # Check if redirection entry
    if '☞' in s:
        # Standardize: **kana【kanji】** ☞ Target
        # E.g. **けりかえす【蹴り返す】** ☞けかえす【蹴返す】
        m = re.match(r'^\*+([^\u300c\u300d\u3010\u3011\[\]]+)(【.*?】|\[.*?\]|「.*?」)\*+\s*☞\s*(.*)', s)
        if m:
            kana = m.group(1).strip()
            kanji = expand_headword_brackets(m.group(2)[1:-1].strip())
            target = m.group(3).strip()
            # Expand target brackets if they contain contractions
            m_tgt = re.search(r'【(.*?)】', target)
            if m_tgt:
                tgt_kanji_expanded = expand_headword_brackets(m_tgt.group(1))
                target = target[:m_tgt.start()] + f"【{tgt_kanji_expanded}】" + target[m_tgt.end():]
            return f"**{kana}【{kanji}】** ☞ {target}"
        return s

    if '＋' not in s and '+' not in s:
        return None
        
    prefix_limit = min(200, len(s))
    prefix = s[:prefix_limit]
    prefix_clean = prefix.replace('**', '')
    
    v1_v2_match = None
    v1_v2_start = -1
    v1_v2_end = -1
    
    for idx in range(len(prefix_clean)):
        if prefix_clean[idx] in ['[', '【', '「', '〔']:
            block, end_idx = extract_balanced_bracket(prefix_clean, idx)
            if block and ('＋' in block or '+' in block):
                v1_v2_match = block
                v1_v2_start = idx
                v1_v2_end = end_idx
                break
                
    if not v1_v2_match:
        return None
        
    before_v1_v2 = prefix_clean[:v1_v2_start].strip()
    
    kanji_match = None
    kanji_start = -1
    kanji_end = -1
    for idx in range(len(before_v1_v2)):
        if before_v1_v2[idx] in ['【', '[', '「', '〔']:
            block, end_idx = extract_balanced_bracket(before_v1_v2, idx)
            if block:
                kanji_match = block
                kanji_start = idx
                kanji_end = end_idx
                break
                
    if not kanji_match:
        return None
        
    kana = before_v1_v2[:kanji_start].strip()
    kanji_content = expand_headword_brackets(kanji_match[1:-1].strip())
    v1_v2_content = v1_v2_match[1:-1].strip()
    
    # Normalize brackets: kanji gets 【】, V1+V2 gets []
    new_header = f"**{kana}【{kanji_content}】**[{v1_v2_content}]"
    
    # Find cut-off point in the original line
    last_char = v1_v2_content[-1]
    pos = -1
    for m in re.finditer(re.escape(last_char), s[:250]):
        sub = s[:m.end()]
        if '＋' in sub or '+' in sub:
            pos = m.end()
            while pos < len(s) and s[pos] in [']', '】', '」', '〕', '*']:
                pos += 1
            break
            
    if pos == -1:
        return None
        
    rest_of_line = s[pos:]
    return new_header + rest_of_line

# --- Noun Form Extraction ---

def extract_noun(line):
    s = line.strip()
    s = re.sub(r'^\*+', '', s)
    s = re.sub(r'\*+$', '', s)
    
    m_pref = re.match(r'^(【?名詞形】?|【?名詞】?)', s)
    if not m_pref:
        return None
    
    s = s[m_pref.end():].strip()
    s = re.sub(r'^(\[\d+\]|❶|①|ⓐ|\d+\.?)\s*', '', s)
    s = s.strip()
    
    m_word = re.match(r'^([\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff]|[a-zA-Z0-9]|\[[\u3040-\u309f]+\])+', s)
    if m_word:
        return m_word.group(0)
    return None

def clean_anki_kanji(s):
    # Remove all bracket contents for kanji form
    # E.g. "遊び疲[づか]れ" -> "遊び疲れ"
    return re.sub(r'\[.*?\]', '', s)

def clean_anki_kana(s):
    # Replace kanji followed by [kana] with just the [kana] to get the reading
    # E.g. "遊び疲[づか]れ" -> "遊びづかれ"
    return re.sub(r'[\u4e00-\u9fff]+\[([\u3040-\u309f\u30a0-\u30ff]+)\]', r'\1', s)

# --- Safe Bolding and Underlining ---

def bold_outside_stars(text, regex):
    # Splits by '**' and applies bolding only to parts that are not already bolded
    parts = text.split('**')
    for idx in range(0, len(parts), 2):
        # Apply regex replacement to parts[idx]
        # We want to find matches and wrap them in '**'
        def repl(m):
            return f"**{m.group(0)}**"
        parts[idx] = regex.sub(repl, parts[idx])
    return '**'.join(parts)

def underline_outside_underlines(text, regex):
    # Splits by '__' and applies underlining only to parts that are not already underlined
    parts = text.split('__')
    for idx in range(0, len(parts), 2):
        def repl(m):
            return f"__{m.group(0)}__"
        parts[idx] = regex.sub(repl, parts[idx])
    return '__'.join(parts)

# --- Main Script Workflow ---

def parse_standard_header_simple(header):
    s = header.strip()
    if not s.startswith('**'):
        return None
        
    idx_kanji_start = s.find('【')
    if idx_kanji_start == -1:
        return None
    
    # Remove leading **
    kana = s[2:idx_kanji_start].strip()
    
    kanji_block, idx_kanji_end = extract_balanced_bracket(s, idx_kanji_start)
    if not kanji_block:
        return None
    
    kanji = kanji_block[1:-1].strip()
    
    idx_v1_v2_start = s.find('[', idx_kanji_end)
    if idx_v1_v2_start == -1:
        return None
        
    v1_v2_block, idx_v1_v2_end = extract_balanced_bracket(s, idx_v1_v2_start)
    if not v1_v2_block:
        return None
        
    v1_v2 = v1_v2_block[1:-1].strip()
    rest = s[idx_v1_v2_end:].strip()
    
    return {
        'kana_block': kana,
        'kanji_block': kanji,
        'v1_v2': v1_v2,
        'rest': rest
    }

def main():
    input_file = r"d:\OCR Project\复合动词\研究社日本語複合動詞活用辞典_gemini.txt"
    output_file = r"d:\OCR Project\复合动词\研究社日本語複合動詞活用辞典_gemini_processed.txt"
    
    if not os.path.exists(input_file):
        print(f"Error: Input file {input_file} does not exist.")
        return
        
    with open(input_file, 'r', encoding='utf-8') as f:
        text = f.read()
        
    # Slice before page 1359 (appendix start)
    main_part = text.split('<1359>')[0]
    
    # --- Pre-cleaning text replacements ---
    
    # 1. Fix typo: **〔例 文** -> 【例文】
    main_part = main_part.replace('**〔例 文**', '【例文】')
    main_part = main_part.replace('**〔例文**', '【例文】')
    
    # 2. Fix the specific split-line headword for でくわす (lines 10972-10974)
    split_dekuwasu_pat = (
        r'\*\*でくわす・でっくわす【出会す・—交す・—喰わす/出っ会す・—交す・—喰わす】\*\* \[出\n'
        r'(<\d+>)\n'
        r'る＋くわす\] ❶'
    )
    main_part = re.sub(
        split_dekuwasu_pat,
        r'\1\n**でくわす・でっくわす【出会す・—交す・—喰わす/出っ会す・—交す・—喰わす】**[出る＋くわす] ❶',
        main_part
    )
    
    # 3. Fix split line for とりちらす (lines 11847-11848)
    main_part = main_part.replace('**とりちらす【取り散らす】**\n☞とりちらかす【取り散らかす】', '**とりちらす【取り散らす】** ☞ とりちらかす【取り散らかす】')
    
    # 4. Fix split line for わきあがる (lines 19337-19338)
    main_part = main_part.replace('**わきあがる【沸き上がる・湧き上がる】**\n「わく＋上がる] ❶', '**わきあがる【沸き上がる・湧き上がる】**[わく＋上がる] ❶')
    
    # 5. Fix V1+V2 brackets that use 〔 〕
    main_part = main_part.replace('**くわえいれる【加え入れる】**〔加える＋入れる]', '**くわえいれる【加え入れる】**[加える＋入れる]')
    main_part = main_part.replace('**たちはだかる【立ち開かる】**〔立つ＋開[はだ]かる]', '**たちはだかる【立ち開かる】**[立つ＋開[はだ]かる]')
    
    # 6. Fix the split-line OCR typo 後[ato / ] on lines 14734-14735 to 後[あと]
    main_part = main_part.replace('後[ato\n]', '後[あと]')
    main_part = main_part.replace('後[ato\n ]', '後[あと]')
    main_part = main_part.replace('後[ato', '後[あと')
    
    # 7. Split the merged adverb header in line 10200 (つきぬく) into a new line
    main_part = main_part.replace(' **【副⁺】', '\n**【副⁺】')
    
    # Let's split into lines and parse entries
    lines = main_part.split('\n')
    
    entries = []
    current_entry = None
    
    for idx, line in enumerate(lines):
        line_strip = line.strip()
        fixed = fix_header(line)
        if fixed:
            # This is a headword line, start new entry
            if current_entry:
                entries.append(current_entry)
            current_entry = {
                'header_line': fixed,
                'content_lines': [],
                'line_num': idx + 1
            }
        else:
            if current_entry:
                current_entry['content_lines'].append(line)
            else:
                pass

    if current_entry:
        entries.append(current_entry)
        
    print(f"Total parsed entries: {len(entries)}")
    
    # --- Process each entry ---
    processed_lines = []
    
    for entry_idx, entry in enumerate(entries):
        header = entry['header_line']
        lines_list = entry['content_lines']
        
        is_redirect = '☞' in header
        
        # Group lines of entry into sections, capturing page markers
        sections = []
        entry_page_markers = []
        current_section_type = 'explanation'
        current_section_lines = []
        
        for line in lines_list:
            line_strip = line.strip()
            if not line_strip:
                continue
                
            # Check for page marker
            if re.match(r'^<\d+>$', line_strip):
                entry_page_markers.append(line_strip)
                continue
                
            # Clean line for checking section type
            line_clean = line_strip
            if line_clean.startswith('**') and line_clean.endswith('**'):
                line_clean = line_clean[2:-2].strip()
            elif line_clean.startswith('**'):
                line_clean = line_clean[2:].strip()
                
            # Check section markers
            is_new = False
            new_type = current_section_type
            
            if line_clean.startswith('【副⁺】') or line_clean.startswith('【副】') or line_clean.startswith('副(') or line_clean.startswith('副（'):
                new_type = 'adverb'
                is_new = True
            elif line_clean.startswith('【例文】') or line_clean.startswith('例文') or line_clean.startswith('【例 文】'):
                if current_section_type in ['noun', 'noun_examples']:
                    new_type = 'noun_examples'
                else:
                    new_type = 'verb_examples'
                is_new = True
            elif line_clean.startswith('【名詞形】') or line_clean.startswith('名詞形') or line_clean.startswith('【名詞】') or line_clean.startswith('名詞'):
                new_type = 'noun'
                is_new = True
            elif line_clean.startswith('【補足】') or line_clean.startswith('補足') or line_clean.startswith('【動】'):
                new_type = 'other'
                is_new = True
                
            if is_new:
                if current_section_lines:
                    sections.append({'type': current_section_type, 'lines': current_section_lines})
                current_section_type = new_type
                current_section_lines = [line_strip]
            else:
                current_section_lines.append(line_strip)
                
        if current_section_lines:
            sections.append({'type': current_section_type, 'lines': current_section_lines})

        if is_redirect:
            # For redirect entries, write out header and merged section texts as-is
            processed_lines.append(header)
            for sec in sections:
                merged_text = "".join(sec['lines'])
                processed_lines.append(merged_text)
            for pm in entry_page_markers:
                processed_lines.append(pm)
            continue
            
        parsed_hdr = parse_standard_header_simple(header)
        if not parsed_hdr:
            processed_lines.append(header)
            for sec in sections:
                merged_text = "".join(sec['lines'])
                processed_lines.append(merged_text)
            for pm in entry_page_markers:
                processed_lines.append(pm)
            continue
            
        kana_block = parsed_hdr['kana_block']
        kanji_block = parsed_hdr['kanji_block']
        v1_v2 = parsed_hdr['v1_v2']
        rest_of_header_line = parsed_hdr['rest']
        
        # Split kana and kanji forms
        kana_forms = [k.strip() for k in re.split(r'[・、/／]', kana_block) if k.strip()]
        kanji_forms = [k.strip() for k in re.split(r'[・、/／]', kanji_block) if k.strip()]
        
        # Build conjugation regexes
        regex_patterns = []
        
        # Pair kana and kanji
        pairs = []
        if len(kana_forms) == len(kanji_forms):
            pairs = list(zip(kanji_forms, kana_forms))
        elif len(kana_forms) == 1:
            pairs = [(kj, kana_forms[0]) for kj in kanji_forms]
        elif len(kanji_forms) == 1:
            pairs = [(kanji_forms[0], kn) for kn in kana_forms]
        else:
            # cross-product fallback
            for kj in kanji_forms:
                for kn in kana_forms:
                    pairs.append((kj, kn))
                    
        for kj, kn in pairs:
            pat = build_verb_regex(kj, kn)
            regex_patterns.append(pat)
            
        # Combine patterns
        combined_verb_pat = "|".join(regex_patterns)
        verb_regex = re.compile(f"\\b(?:{combined_verb_pat})\\b" if regex_patterns else "")
        if regex_patterns:
            verb_regex = re.compile(f"(?:{combined_verb_pat})")
            
        # Also build a simple regex for exact dictionary forms (to bold in explanation/adverbs)
        dict_patterns = []
        for kj in kanji_forms:
            dict_patterns.append(make_split_regex(kj))
        for kn in kana_forms:
            dict_patterns.append(make_split_regex(kn))
        dict_regex = re.compile(f"(?:{'|'.join(dict_patterns)})")
        
        # Process header line (which contains the explanation at the end)
        # Bold dictionary form in the explanation part
        if rest_of_header_line.strip():
            rest_bold = bold_outside_stars(rest_of_header_line, dict_regex)
            header_processed = f"**{kana_block}【{kanji_block}】**[{v1_v2}]{rest_bold}"
        else:
            header_processed = header
            
        processed_lines.append(header_processed)
        
        noun_regex = None # compiled when we see a noun form
        
        for sec in sections:
            stype = sec['type']
            lines = sec['lines']
            merged_text = "".join(lines)
            
            # Process the text based on the section type
            if stype == 'explanation' or stype == 'adverb':
                # Rule A: Bold headwords
                line_processed = bold_outside_stars(merged_text, dict_regex)
            elif stype == 'verb_examples':
                # Rule B: Underline verb conjugations
                line_processed = underline_outside_underlines(merged_text, verb_regex)
            elif stype == 'noun':
                # Rule C: Bold the noun form in the section header line
                # Strip outer ** and clean existing ** first to prevent double-bolding
                line_clean = merged_text.replace('**', '').strip()
                
                noun_raw = extract_noun(line_clean)
                if noun_raw:
                    noun_kj = clean_anki_kanji(noun_raw)
                    noun_kn = clean_anki_kana(noun_raw)
                    noun_pat = build_noun_regex(noun_kj, noun_kn)
                    noun_regex = re.compile(noun_pat)
                    
                    escaped_noun = re.escape(noun_raw)
                    m_noun = re.search(escaped_noun, line_clean)
                    if m_noun:
                        start, end = m_noun.span()
                        line_processed = line_clean[:start] + f"**{noun_raw}**" + line_clean[end:]
                    else:
                        line_processed = line_clean
                else:
                    line_processed = line_clean
            elif stype == 'noun_examples':
                # Rule D: Underline noun occurrences in noun examples
                if noun_regex:
                    line_processed = underline_outside_underlines(merged_text, noun_regex)
                else:
                    line_processed = merged_text
            else:
                line_processed = merged_text
                
            processed_lines.append(line_processed)
            
    # Write output file
    with open(output_file, 'w', encoding='utf-8') as f:
        f.write("\n".join(processed_lines))
        
    print(f"Processed file written to {output_file}. Total lines: {len(processed_lines)}")

if __name__ == '__main__':
    main()
