Загрузка данных


# -*- coding: utf-8 -*-
"""
Модуль управления учетными записями пользователей.
Реализует функционал для роли 'Администратор'.
Работает с базой db/users.db.
"""
import hashlib
import os
import sqlite3
import tkinter as tk
import uuid
from datetime import datetime
from tkinter import filedialog, messagebox, ttk

from gui.main_interface import MainInterface # Для смены пользователя
from modules.utils.window_behavior import LevelTwoBehavior


def center_on_parent(child_window):
    child_window.update_idletasks()
    window_width = child_window.winfo_reqwidth()
    window_height = child_window.winfo_reqheight()
    
    if child_window.master is not None:
        parent = child_window.master
        x = parent.winfo_rootx() + (parent.winfo_width() // 2) - (window_width // 2)
        y = parent.winfo_rooty() + (parent.winfo_height() // 2) - (window_height // 2)
    else:
        screen_width = child_window.winfo_screenwidth()
        screen_height = child_window.winfo_screenheight()
        x = (screen_width // 2) - (window_width // 2)
        y = (screen_height // 2) - (window_height // 2)
        
    child_window.geometry(f"+{x}+{y}")


class AddUserDialog(tk.Toplevel):
    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.title("Добавление пользователя")
        self.result = None
        self.protocol("WM_DELETE_WINDOW", self.cancel)
        self.resizable(False, False)
        self.geometry("465x365")
        
        frame = ttk.Frame(self, padding="20")
        frame.pack(expand=True, fill="both")
        
        # Логин
        ttk.Label(frame, text="Логин:").grid(row=0, column=0, sticky="w", pady=5)
        self.entry_username = ttk.Entry(frame)
        self.entry_username.grid(row=0, column=1, padx=10, pady=5, sticky="ew")
        
        # Роль
        ttk.Label(frame, text="Роль:").grid(row=1, column=0, sticky="w", pady=5)
        self.combobox_role = ttk.Combobox(
            frame, values=["Администратор", "Эксперт", "Пользователь"]
        )
        self.combobox_role.grid(row=1, column=1, padx=10, pady=5, sticky="ew")
        
        # Пароль
        ttk.Label(frame, text="Пароль:").grid(row=2, column=0, sticky="w", pady=5)
        self.entry_password = ttk.Entry(frame, show="*")
        self.entry_password.grid(row=2, column=1, padx=10, pady=5, sticky="ew")
        
        # Кнопки
        btn_frame = ttk.Frame(frame)
        btn_frame.grid(row=3, columnspan=2, pady=20)
        ttk.Button(btn_frame, text="OK", command=self.ok).pack(side="left", padx=10)
        ttk.Button(btn_frame, text="Отмена", command=self.cancel).pack(side="right", padx=10)
        
        for i in range(2):
            frame.columnconfigure(i, weight=1)
            
    def ok(self):
        username = self.entry_username.get().strip()
        role = self.combobox_role.get().strip()
        password = self.entry_password.get()
        
        if username and role and password:
            salt = uuid.uuid4().hex
            hashed_password = hashlib.sha256((password + salt).encode()).hexdigest()
            self.result = (username, role, hashed_password, salt)
            self.destroy()
        else:
            messagebox.showwarning("Ошибка", "Все поля обязательны для заполнения!")

    def cancel(self):
        self.result = None
        self.destroy()


class UsersWindow(tk.Toplevel):
    def __init__(self, parent, main_app_reference=None):
        super().__init__(parent)
        self.parent = parent
        self.main_app = main_app_reference
        self.title("Пользователи")
        self.geometry("465x365")
        
        behavior = LevelTwoBehavior()
        behavior.setup_level_two(parent)
        
        self.build_ui()

    def build_ui(self):
        container = ttk.Frame(self)
        container.pack(fill="both", expand=True, padx=10, pady=10)
        
        left_panel = ttk.Frame(container)
        left_panel.pack(side="left", fill="y", padx=(0, 10))
        
        right_panel = ttk.Frame(container)
        right_pack = right_panel.pack(side="right", fill="both", expand=True)
        
        # Кнопки действий
        actions = [
            ("Добавить пользователя", self.add_user),
            ("Сменить пользователя", self.change_user),
            ("Удалить пользователя", self.remove_user)
        ]
        for text, cmd in actions:
            ttk.Button(left_panel, text=text, command=cmd).pack(fill="x", pady=5)
            
        # Таблица
        columns = ("ID", "Логин", "Роль")
        self.users_tree = ttk.Treeview(right_panel, columns=columns, show="headings")
        for col in columns:
            self.users_tree.heading(col, text=col)
            self.users_tree.column(col, anchor="center", width=100)
        self.users_tree.pack(fill="both", expand=True)
        
        self.load_users()

    def load_users(self):
        try:
            conn = sqlite3.connect("users.db")
            cursor = conn.cursor()
            cursor.execute("SELECT id, username, role FROM users ORDER BY id ASC;")
            rows = cursor.fetchall()
            
            for i in self.users_tree.get_children():
                self.users_tree.delete(i)
                
            for row in rows:
                self.users_tree.insert("", "end", values=row)
            conn.close()
        except Exception as e:
            messagebox.showerror("DB Error", f"Не удалось загрузить базу данных.\n{e}")

    def add_user(self):
        dialog = AddUserDialog(self)
        self.wait_window(dialog)
        if dialog.result:
            username, role, pwd_hash, salt = dialog.result
            with sqlite3.connect("users.db") as conn:
                cursor = conn.cursor()
                cursor.execute(
                    "INSERT INTO users (username, role, password_hash, salt) VALUES (?,?,?,?)",
                    (username, role, pwd_hash, salt)
                )
                conn.commit()
            self.load_users()

    def change_user(self):
        """
        Открывает диалоговое окно для смены/выбора пользователя.
        После успешного входа закрывает само окно управления пользователями.
        """
        # Передаем САМО ОКНО (self) как родителя для авторизации
        login_dialog = LoginDialog(parent=self)
        
        self.wait_window(login_dialog)
        
        if hasattr(login_dialog, 'successful') and login_dialog.successful:
            messagebox.showinfo("Успех", "Пользователь сменен.")
            
            # Если у нас есть ссылка на главное приложение, сообщаем ему о выходе
            if self.main_app:
                try:
                    enable_app(self.main_app) # Разблокируем главный экран
                except Exception:
                    pass
                    
            self.destroy() # Закрываем окно списка пользователей

    def remove_user(self):
        selected_item = self.users_tree.selection()
        if not selected_item:
            messagebox.showwarning("Ошибка", "Выберите пользователя для удаления!")
            return
            
        user_id = int(self.users_tree.item(selected_item)["values"][0])
        
        answer = messagebox.askyesno(
            "Подтверждение", f"Удалить пользователя с ID={user_id}?\nВнимание: ID будут пересчитаны."
        )
        if not answer:
            return
            
        try:
            with sqlite3.connect("users.db") as conn:
                cursor = conn.cursor()
                cursor.execute("DELETE FROM users WHERE id=?", (user_id,))
                
                # Перенумерация
                cursor.execute("SELECT * FROM users ORDER BY id ASC;")
                rows = cursor.fetchall()
                
                new_id = 1
                for row in rows:
                    old_id = row[0]
                    cursor.execute("UPDATE users SET id=? WHERE id=?", (new_id, old_id))
                    new_id += 1
                    
                cursor.execute("DELETE FROM sqlite_sequence WHERE name='users';")
                conn.commit()
            self.load_users()
        except Exception as e:
            messagebox.showerror("Error", str(e))