#!/usr/bin/env python3
"""
Разовый скрипт (запускается один раз вручную, не сервис).

Делает две вещи:
1. Берёт ВСЕ бонусные слова со ВСЕХ уровней и проверяет, не подходят
   ли они ещё и к другим уровням (по буквам колеса) — если подходят
   и их там ещё нет, добавляет.
2. Ищет уровни, где бонусное слово физически НЕЛЬЗЯ составить из букв
   этого же уровня — то есть слово было добавлено туда по ошибке
   (например, вручную, не тем ботом).

Буквы "е" и "ё" считаются одной и той же буквой (как и в bot_listener.py).

Запуск на сервере:
    python3 fix_bonus_words.py
"""

import json
import os
from collections import Counter

LEVELS_DIR = "/home/WordsGame/levels"


def normalize(s: str) -> str:
    return s.upper().replace("Ё", "Е")


def can_build(word: str, letters: list) -> bool:
    word_count = Counter(normalize(word))
    letters_count = Counter(normalize(l) for l in letters)
    for ch, cnt in word_count.items():
        if letters_count.get(ch, 0) < cnt:
            return False
    return True


def load_all_levels():
    levels = []
    for filename in sorted(os.listdir(LEVELS_DIR)):
        if not filename.endswith(".json"):
            continue
        path = os.path.join(LEVELS_DIR, filename)
        try:
            with open(path, encoding="utf-8") as f:
                level = json.load(f)
            levels.append((path, level))
        except Exception as e:
            print(f"НЕ УДАЛОСЬ ОТКРЫТЬ {filename}: {e}")
    return levels


def main():
    all_levels = load_all_levels()
    print(f"Загружено уровней: {len(all_levels)}\n")

    # ---- Шаг 1: ищем ошибочные бонусные слова (не подходят под свой же уровень) ----
    print("=" * 60)
    print("ШАГ 1: слова, которые НЕ подходят под буквы своего уровня")
    print("=" * 60)
    broken_found = False
    for path, level in all_levels:
        for word in level["bonusWords"]:
            if not can_build(word, level["letters"]):
                broken_found = True
                print(
                    f"  Уровень {level['id']} ({os.path.basename(path)}): "
                    f"бонусное слово \"{word}\" НЕ составляется из букв "
                    f"\"{''.join(level['letters'])}\""
                )
    if not broken_found:
        print("  Ничего не найдено — все бонусные слова корректны.")
    print()

    # ---- Шаг 2: собираем все уникальные бонусные слова со всех уровней ----
    all_bonus_words = set()
    for _, level in all_levels:
        for word in level["bonusWords"]:
            all_bonus_words.add(word.upper())

    print(f"Уникальных бонусных слов по всей базе: {len(all_bonus_words)}\n")

    # ---- Шаг 3: пробуем добавить каждое слово во все подходящие уровни ----
    print("=" * 60)
    print("ШАГ 2: расширяем бонусные слова по всем подходящим уровням")
    print("=" * 60)

    total_added = 0
    report = []  # (word, [level_ids])

    for word in sorted(all_bonus_words):
        added_to = []
        for path, level in all_levels:
            existing_grid = {normalize(w["word"]) for w in level["gridWords"]}
            existing_bonus = {normalize(w) for w in level["bonusWords"]}
            target = normalize(word)

            if target in existing_grid or target in existing_bonus:
                continue
            if not can_build(word, level["letters"]):
                continue

            level["bonusWords"].append(word)
            added_to.append(level["id"])
            total_added += 1

        if added_to:
            report.append((word, added_to))

    # сохраняем все изменённые файлы
    for path, level in all_levels:
        with open(path, "w", encoding="utf-8") as f:
            json.dump(level, f, ensure_ascii=False, indent=2)

    if report:
        for word, ids in report:
            print(f"  \"{word}\" -> добавлено в уровни: {', '.join(map(str, ids))}")
    else:
        print("  Новых добавлений не потребовалось — всё уже было расставлено оптимально.")

    print()
    print("=" * 60)
    print(f"ИТОГО: добавлено {total_added} новых записей бонусных слов")
    print("=" * 60)


if __name__ == "__main__":
    main()