import requests
import os
import json
import tkinter as tk
from tkinter import messagebox, Toplevel
from tkinter.ttk import Progressbar
from threading import Thread

CONFIG_FILE = 'config.json'

# === Загрузка и сохранение конфигурации ===
def load_config():
    if not os.path.exists(CONFIG_FILE):
        default_config = {
            "mc_version": "",
            "mod_loader": "Forge",
            "default_keywords": [],
            "download_folder": "mods",
            "user_agent": "ModPackBuilder/1.0 (+contact@yourdomain.com)",
        }
        with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
            json.dump(default_config, f, indent=4, ensure_ascii=False)
    with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
        return json.load(f)

def save_config(config):
    with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
        json.dump(config, f, indent=4, ensure_ascii=False)

# === Modrinth API запросы и обработка ===
def search_mods_modrinth(description, mc_version, modloader, user_agent):
    url = 'https://api.modrinth.com/v2/search'
    headers = {'User-Agent': user_agent}
    facets = [
        [f"versions:{mc_version}"],
        [f"project_type:mod"],
        [f"categories:{modloader.lower()}"],
    ]
    params = {'query': description, 'facets': json.dumps(facets), 'limit': 100}
    response = requests.get(url, params=params, headers=headers)
    if response.status_code == 200:
        return response.json().get('hits', [])
    else:
        messagebox.showerror("Ошибка", f"Modrinth API Ошибка: {response.status_code}")
        return []

def get_latest_compatible_version_modrinth(mod_id, mc_version, modloader, user_agent):
    url = f'https://api.modrinth.com/v2/project/{mod_id}/version'
    headers = {'User-Agent': user_agent}
    response = requests.get(url, headers=headers)
    if response.status_code == 200:
        versions = [v for v in response.json()
                    if mc_version in v['game_versions'] and
                    modloader.lower() in [loader.lower() for loader in v['loaders']]]
        if versions:
            for version in versions:
                if version['version_type'] == 'release':
                    return version
            return versions[0]
    return None

def get_mod_dependencies(version):
    dependencies = version.get('dependencies', [])
    required = []
    for dep in dependencies:
        if dep['dependency_type'] == 'required':
            required.append(dep['project_id'])
    return required

def get_mod_conflicts(version):
    # Modrinth API не предоставляет информацию о конфликтах модов напрямую,
    # поэтому эта функция будет расширена, если такая информация станет доступной.
    return []

def get_mod_by_id(mod_id):
    url = f'https://api.modrinth.com/v2/project/{mod_id}'
    headers = {'User-Agent': config["user_agent"]}
    response = requests.get(url, headers=headers)
    if response.status_code == 200:
        return response.json()
    return None

# === Функция загрузки файла ===
def download_file(url, dest_folder, filename, progress_bar, user_agent):
    local_filename = os.path.join(dest_folder, filename)
    headers = {'User-Agent': user_agent}
    with requests.get(url, stream=True, headers=headers) as r:
        r.raise_for_status()
        total_size = int(r.headers.get('content-length', 0))
        with open(local_filename, 'wb') as f:
            downloaded = 0
            for chunk in r.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)
                    downloaded += len(chunk)
                    if total_size > 0:
                        progress = (downloaded / total_size) * 100
                        progress_bar['value'] = progress
                        progress_bar.update()
    return local_filename

# === GUI ===
class ModPackBuilder:
    def __init__(self, root, config):
        self.root = root
        self.config = config
        self.root.title("ModPack Builder")

        # Разрешаем изменение размера окна
        self.root.resizable(True, True)

        # Создаем общий фрейм для размещения виджетов
        self.main_frame = tk.Frame(root)
        self.main_frame.pack(fill=tk.BOTH, expand=True)

        # === Поля для ввода данных ===
        self.mc_version_var = tk.StringVar(value=config["mc_version"])
        self.modloader_var = tk.StringVar(value=config.get("mod_loader", "Forge"))
        self.description_var = tk.StringVar(value=", ".join(config["default_keywords"]))

        # Используем grid с весом для автоматической подстройки
        self.main_frame.columnconfigure(1, weight=1)
        self.main_frame.rowconfigure(5, weight=1)  # Список модов будет расширяться

        tk.Label(self.main_frame, text="Версия Minecraft:").grid(row=0, column=0, sticky="w", padx=5, pady=5)
        tk.Entry(self.main_frame, textvariable=self.mc_version_var).grid(row=0, column=1, sticky="ew", padx=5, pady=5)

        tk.Label(self.main_frame, text="Mod Loader:").grid(row=1, column=0, sticky="w", padx=5, pady=5)
        tk.OptionMenu(self.main_frame, self.modloader_var, "Forge", "Fabric", "Quilt", "Liteloader", "Cauldron").grid(row=1, column=1, sticky="ew", padx=5, pady=5)

        tk.Label(self.main_frame, text="Описание (ключевые слова):").grid(row=2, column=0, sticky="w", padx=5, pady=5)
        tk.Entry(self.main_frame, textvariable=self.description_var).grid(row=2, column=1, sticky="ew", padx=5, pady=5)

        tk.Button(self.main_frame, text="Поиск модов", command=self.search_mods).grid(row=3, column=0, columnspan=2, pady=5)

        # === Список модов ===
        self.mods_list = tk.Listbox(self.main_frame, selectmode="multiple")
        self.mods_list.grid(row=5, column=0, columnspan=2, sticky="nsew", padx=5, pady=5)

        self.scrollbar = tk.Scrollbar(self.main_frame, orient="vertical", command=self.mods_list.yview)
        self.scrollbar.grid(row=5, column=2, sticky="ns")
        self.mods_list.config(yscrollcommand=self.scrollbar.set)

        # === Кнопки ===
        tk.Button(self.main_frame, text="Проверить совместимость", command=self.check_compatibility).grid(row=6, column=0, columnspan=2, pady=5)
        tk.Button(self.main_frame, text="Скачать выбранные моды", command=self.download_selected_mods).grid(row=7, column=0, columnspan=2, pady=5)

        # При изменении размера окна обновляем размеры виджетов
        self.root.bind('<Configure>', self.on_resize)

        # Словарь для хранения информации о модах и их зависимостях
        self.selected_mods_info = {}

    def on_resize(self, event):
        # Обновляем размеры виджетов при изменении размера окна
        self.main_frame.update_idletasks()

    def search_mods(self):
        description = self.description_var.get()
        mc_version = self.mc_version_var.get()
        modloader = self.modloader_var.get()

        self.config["mc_version"] = mc_version
        self.config["mod_loader"] = modloader
        self.config["default_keywords"] = description.split(", ")
        save_config(self.config)

        self.mods_list.delete(0, tk.END)
        self.mods = []

        mods = search_mods_modrinth(description, mc_version, modloader, self.config["user_agent"])
        for mod in mods:
            self.mods.append({"source": "Modrinth", "data": mod})
            self.mods_list.insert(tk.END, f"{mod['title']} - {mod['description']}")

    def check_compatibility(self):
        selected_indices = self.mods_list.curselection()
        if not selected_indices:
            messagebox.showwarning("Предупреждение", "Выберите хотя бы один мод для проверки.")
            return

        mc_version = self.config["mc_version"]
        modloader = self.config["mod_loader"].lower()

        incompatible_mods = []
        mod_map = {}

        # Получение информации о выбранных модах и их версиях
        for idx in selected_indices:
            mod_entry = self.mods[idx]
            mod = mod_entry["data"]
            mod_id = mod['project_id']
            mod_title = mod['title']
            version = get_latest_compatible_version_modrinth(mod_id, mc_version, modloader, self.config["user_agent"])
            if version:
                mod_map[mod_id] = {
                    'title': mod_title,
                    'version': version,
                    'dependencies': get_mod_dependencies(version),
                }
            else:
                incompatible_mods.append(mod_title)

        if incompatible_mods:
            messagebox.showerror("Несовместимые моды", f"Следующие моды не совместимы с версией {mc_version} и загрузчиком {modloader.capitalize()}:\n" + "\n".join(incompatible_mods))
            return

        # Проверка зависимостей и автоматическое добавление недостающих
        missing_dependencies = []
        processed_mods = set()
        mods_to_process = list(mod_map.keys())

        while mods_to_process:
            current_mods = mods_to_process
            mods_to_process = []
            for mod_id in current_mods:
                if mod_id in processed_mods:
                    continue
                mod_info = mod_map[mod_id]
                processed_mods.add(mod_id)
                for dep_id in mod_info['dependencies']:
                    if dep_id not in mod_map:
                        dep_project = get_mod_by_id(dep_id)
                        if dep_project:
                            dep_title = dep_project['title']
                            dep_version = get_latest_compatible_version_modrinth(dep_id, mc_version, modloader, self.config["user_agent"])
                            if dep_version:
                                # Добавляем зависимость в список модов
                                mod_map[dep_id] = {
                                    'title': dep_title,
                                    'version': dep_version,
                                    'dependencies': get_mod_dependencies(dep_version),
                                }
                                self.mods.append({"source": "Modrinth", "data": dep_project})
                                self.mods_list.insert(tk.END, f"{dep_title} - {dep_project['description']}")
                                # Отмечаем зависимость как выбранную
                                self.mods_list.select_set(tk.END)
                                # Добавляем в список для дальнейшей обработки зависимостей этой зависимости
                                mods_to_process.append(dep_id)
                            else:
                                missing_dependencies.append(dep_title)
                        else:
                            missing_dependencies.append(dep_id)

        if missing_dependencies:
            messagebox.showwarning("Отсутствующие зависимости", f"Следующие зависимости отсутствуют или несовместимы:\n" + "\n".join(set(missing_dependencies)))
            return

        # Сохраняем информацию о выбранных модах и их зависимостях для дальнейшего использования при скачивании
        self.selected_mods_info = mod_map

        messagebox.showinfo("Совместимость", "Выбранные моды совместимы между собой и с текущей версией Minecraft. Все необходимые зависимости будут скачаны.")

    def download_selected_mods(self):
        if not self.selected_mods_info:
            messagebox.showwarning("Предупреждение", "Сначала выполните проверку совместимости.")
            return

        if not os.path.exists(self.config["download_folder"]):
            os.makedirs(self.config["download_folder"])

        self.progress_window = Toplevel(self.root)
        self.progress_window.title("Прогресс скачивания")

        progress_bar = Progressbar(self.progress_window, orient="horizontal", length=300, mode="determinate")
        progress_bar.pack(pady=20)

        def download():
            mod_items = list(self.selected_mods_info.items())
            total = len(mod_items)
            for idx, (mod_id, mod_info) in enumerate(mod_items):
                mod_title = mod_info['title']
                version = mod_info['version']
                files = [file for file in version['files'] if file['primary']]
                if files:
                    file_info = files[0]
                    filename = file_info['filename']
                    url = file_info['url']
                    download_file(url, self.config["download_folder"], filename, progress_bar, self.config["user_agent"])
                else:
                    messagebox.showinfo("Информация", f"Нет файлов для мода {mod_title}.")

                progress = ((idx + 1) / total) * 100
                progress_bar['value'] = progress
                progress_bar.update()

            messagebox.showinfo("Готово", "Все моды и их зависимости скачаны!")
            self.progress_window.destroy()

        Thread(target=download).start()

# === Запуск приложения ===
if __name__ == '__main__':
    config = load_config()
    root = tk.Tk()
    app = ModPackBuilder(root, config)
    root.mainloop()