#!/usr/bin/env python3
"""Модуль исправления структуры Python-проекта.

Автоматически выполняет:
1. Удаление BOM-маркеров из файлов
2. Создание недостающих __init__.py
3. Логирование всех операций

Использование: python fix_python_project.py [путь_к_проекту]
"""

import os
import sys
from typing import List, Set


# Настройки обработки проекта
INIT_IGNORED_DIRS: Set[str] = {'tests', '.git', '__pycache__', 'venv', 'env', '.idea', '.vscode'}
TARGET_EXTENSIONS: tuple = ('.py', '.json', '.txt')
INIT_TEMPLATE: str = """# Auto-generated by fix_python_project.py
\"\"\"Package initialization.\"\"\"
"""


class ProjectFixer:
    """Исправляет структуру Python-проекта.

    Атрибуты:
        root_dir: Корневая директория проекта.
        log: Лог выполненных операций.
    """

    def __init__(self, root_dir: str = '.'):
        """Инициализирует экземпляр ProjectFixer.

        Args:
            root_dir: Корневая директория проекта. По умолчанию '.'.
        """

        self.root_dir = os.path.abspath(root_dir)
        self.log: List[str] = []

    def remove_bom(self, filepath: str) -> bool:
        """Удаляет BOM-маркер из файла.

        Args:
            filepath: Путь к файлу.

        Returns:
            bool: True если BOM был удалён.

        Raises:
            Exception: При ошибке чтения/записи.
        """

        try:
            with open(filepath, 'rb') as f:
                content = f.read()

            if content.startswith(b'\xEF\xBB\xBF'):
                with open(filepath, 'wb') as f:
                    f.write(content[3:])
                self.log.append(f"REMOVED BOM: {filepath}")
                return True
        except Exception as e:
            self.log.append(f"ERROR processing {filepath}: {str(e)}")
        return False

    def should_skip_init(self, dir_path: str) -> bool:
        """Проверяет нужно ли пропустить директорию.

        Args:
            dir_path: Путь к директории.

        Returns:
            bool: True если директорию нужно пропустить.
        """

        dir_name = os.path.basename(dir_path)
        return (dir_name in INIT_IGNORED_DIRS or
                dir_name.startswith('.'))

    def needs_init_py(self, dir_path: str) -> bool:
        """Проверяет нужно ли создать __init__.py.

        Args:
            dir_path: Путь к директории.

        Returns:
            bool: True если __init__.py требуется.
        """

        if self.should_skip_init(dir_path):
            return False

        try:
            items = os.listdir(dir_path)
            has_py_files = any(f.endswith('.py') and f != '__init__.py' for f in items)
            has_init = '__init__.py' in items
            return has_py_files and not has_init
        except Exception:
            return False

    def create_init_py(self, dir_path: str) -> bool:
        """Создаёт файл __init__.py.

        Args:
            dir_path: Путь к директории.

        Returns:
            bool: True если файл создан.

        Raises:
            Exception: При ошибке создания.
        """

        init_path = os.path.join(dir_path, '__init__.py')
        try:
            with open(init_path, 'w', encoding='utf-8') as f:
                f.write(INIT_TEMPLATE)
            self.log.append(f"CREATED INIT: {init_path}")
            return True
        except Exception as e:
            self.log.append(f"ERROR creating {init_path}: {str(e)}")
            return False

    def process_directory(self):
        """Рекурсивно обрабатывает проект."""

        for root, dirs, files in os.walk(self.root_dir):
            # Обработка файлов с целевыми расширениями
            for file in files:
                if file.endswith(TARGET_EXTENSIONS):
                    self.remove_bom(os.path.join(root, file))

            # Создание __init__.py где это необходимо
            if self.needs_init_py(root):
                self.create_init_py(root)

    def save_log(self, log_file: str = 'project_fix.log'):
        """Сохраняет лог операций в файл.

        Args:
            log_file: Имя файла лога. По умолчанию 'project_fix.log'.
        """

        with open(log_file, 'w', encoding='utf-8') as f:
            f.write("\n".join(self.log))


if __name__ == '__main__':
    # Обработка аргументов командной строки
    target_dir = sys.argv[1] if len(sys.argv) > 1 else '.'

    # Инициализация и запуск обработки
    fixer = ProjectFixer(target_dir)
    print(f"Исправление структуры проекта в: {fixer.root_dir}")

    fixer.process_directory()
    fixer.save_log()

    # Вывод результатов
    print(f"Готово! Внесено {len(fixer.log)} изменений.")
    print("Подробности сохранены в project_fix.log")
