import keyring
import json
import os
from pathlib import Path
from typing import List, Optional, Dict, Any
import logging

import re
import unicodedata


def safe_filename(text, substitute="_"):
    # Нормализуем Unicode-символы (например, убираем акценты)
    text = unicodedata.normalize("NFKD", text).encode("ascii", "ignore").decode("ascii")

    # Заменяем недопустимые символы на substitute
    cleaned = re.sub(r'[<>:"/\\|?*\x00-\x1f]', substitute, text)

    # Удаляем начальные и конечные пробелы/точки
    cleaned = cleaned.strip(" .")

    # Проверяем на зарезервированные имена (Windows)
    reserved = {
        "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5",
        "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5",
        "LPT6", "LPT7", "LPT8", "LPT9"
    }
    if cleaned.upper() in reserved:
        cleaned = substitute + cleaned

    # Если строка пустая после обработки
    return cleaned if cleaned else "unnamed"


class CredentialManager:
    def __init__(self, service_name: str):
        self.service_name = service_name
        self.users_file = self._get_users_file_path()
        self._ensure_users_file()

    def _get_users_file_path(self) -> Path:
        """Получает путь к файлу с пользователями в AppData"""
        appdata_path = os.getenv('APPDATA') or os.path.expanduser('~')
        config_dir = Path(appdata_path) / "SucroseLauncher" / "creds"
        config_dir.mkdir(exist_ok=True)
        return config_dir / f"{safe_filename(self.service_name)}.json"

    def _ensure_users_file(self) -> None:
        """Создает файл пользователей, если он не существует"""
        if not self.users_file.exists():
            with open(self.users_file, 'w', encoding='utf-8') as f:
                json.dump([], f)

    def _load_users(self) -> List[Dict[str, Any]]:
        """Загружает список пользователей из файла"""
        try:
            with open(self.users_file, 'r', encoding='utf-8') as f:
                return json.load(f)
        except (json.JSONDecodeError, FileNotFoundError) as e:
            logging.warning(f"Failed to load users file: {e}")
            return []

    def _save_users(self, users: List[Dict[str, Any]]) -> None:
        """Сохраняет список пользователей в файл"""
        try:
            with open(self.users_file, 'w', encoding='utf-8') as f:
                json.dump(users, f, indent=2, ensure_ascii=False)
        except Exception as e:
            logging.error(f"Failed to save users file: {e}")

    def _find_user_index(self, username: str) -> int:
        """Находит индекс пользователя в списке"""
        users = self._load_users()
        for i, user_data in enumerate(users):
            if user_data.get('username') == username:
                return i
        return -1

    def save_credentials(self, username: str, password: str, metadata: Optional[Dict[str, Any]] = None) -> bool:
        """Сохраняет пароль и информацию о пользователе"""
        try:
            # Сохраняем пароль в Credential Manager
            keyring.set_password(self.service_name, username, password)

            # Обновляем информацию о пользователе
            users = self._load_users()
            user_index = self._find_user_index(username)

            user_data = {
                'username': username,
                'last_saved': os.path.getmtime(self.users_file),
                'metadata': metadata or {}
            }

            if user_index >= 0:
                # Обновляем существующего пользователя
                users[user_index].update(user_data)
            else:
                # Добавляем нового пользователя
                users.append(user_data)

            self._save_users(users)
            return True

        except Exception as e:
            logging.error(f"Failed to save credentials for {username}: {e}")
            return False

    def get_credentials(self, username: str) -> Optional[str]:
        """Получает пароль для указанного пользователя"""
        try:
            return keyring.get_password(self.service_name, username)
        except Exception as e:
            logging.error(f"Failed to get credentials for {username}: {e}")
            return None

    def get_user_metadata(self, username: str) -> Optional[Dict[str, Any]]:
        """Получает метаданные пользователя"""
        users = self._load_users()
        user_index = self._find_user_index(username)
        if user_index >= 0:
            return users[user_index].get('metadata', {})
        return None

    def update_user_metadata(self, username: str, metadata: Dict[str, Any]) -> bool:
        """Обновляет метаданные пользователя"""
        try:
            users = self._load_users()
            user_index = self._find_user_index(username)

            if user_index >= 0:
                users[user_index]['metadata'] = metadata
                self._save_users(users)
                return True
            return False

        except Exception as e:
            logging.error(f"Failed to update metadata for {username}: {e}")
            return False

    def delete_credentials(self, username: str) -> bool:
        """Удаляет сохраненные данные пользователя"""
        try:
            # Удаляем из Credential Manager
            keyring.delete_password(self.service_name, username)

            # Удаляем из файла пользователей
            users = self._load_users()
            user_index = self._find_user_index(username)

            if user_index >= 0:
                users.pop(user_index)
                self._save_users(users)

            return True

        except keyring.errors.PasswordDeleteError:
            logging.warning(f"Password for {username} not found in keyring")
            return False
        except Exception as e:
            logging.error(f"Failed to delete credentials for {username}: {e}")
            return False

    def get_saved_usernames(self) -> List[str]:
        """Возвращает список всех сохраненных имен пользователей"""
        users = self._load_users()
        return [user['username'] for user in users if 'username' in user]

    def get_all_users_data(self) -> List[Dict[str, Any]]:
        """Возвращает полные данные всех пользователей"""
        return self._load_users()

    def get_last_saved_user(self) -> Optional[str]:
        """Возвращает имя последнего сохраненного пользователя"""
        users = self._load_users()
        if not users:
            return None

        # Сортируем по времени сохранения (новейшие сначала)
        sorted_users = sorted(users, key=lambda x: x.get('last_saved', 0), reverse=True)
        return sorted_users[0]['username']

    def user_exists(self, username: str) -> bool:
        """Проверяет, существует ли пользователь"""
        return self._find_user_index(username) >= 0

    def validate_credentials(self, username: str) -> bool:
        """Проверяет, что учетные данные пользователя существуют и валидны"""
        if not self.user_exists(username):
            return False

        password = self.get_credentials(username)
        return password is not None and len(password) > 0

    def get_users_count(self) -> int:
        """Возвращает количество сохраненных пользователей"""
        return len(self._load_users())

    def clear_all_credentials(self) -> bool:
        """Очищает все сохраненные учетные данные"""
        try:
            users = self._load_users()

            # Удаляем все пароли из Credential Manager
            for user_data in users:
                username = user_data.get('username')
                if username:
                    try:
                        keyring.delete_password(self.service_name, username)
                    except keyring.errors.PasswordDeleteError:
                        pass  # Игнорируем если пароль уже удален

            # Очищаем файл пользователей
            self._save_users([])
            return True

        except Exception as e:
            logging.error(f"Failed to clear all credentials: {e}")
            return False

    def backup_users_data(self, backup_path: Optional[Path] = None) -> bool:
        """Создает резервную копию данных пользователей"""
        try:
            if backup_path is None:
                backup_path = self.users_file.with_suffix('.json.backup')

            users_data = self._load_users()
            with open(backup_path, 'w', encoding='utf-8') as f:
                json.dump(users_data, f, indent=2, ensure_ascii=False)

            return True

        except Exception as e:
            logging.error(f"Failed to create backup: {e}")
            return False

    def restore_users_data(self, backup_path: Path) -> bool:
        """Восстанавливает данные пользователей из резервной копии"""
        try:
            with open(backup_path, 'r', encoding='utf-8') as f:
                users_data = json.load(f)

            self._save_users(users_data)
            return True

        except Exception as e:
            logging.error(f"Failed to restore from backup: {e}")
            return False


# Пример использования
if __name__ == "__main__":
    # Инициализация менеджера
    cred_manager = CredentialManager("SucroseLauncher")

    # Сохранение учетных данных
    cred_manager.save_credentials(
        "player123",
        "my_password_123",
        {"last_login": "2024-01-15", "favorite_servers": ["server1", "server2"]}
    )

    # Получение списка пользователей
    users = cred_manager.get_saved_usernames()
    print(f"Сохраненные пользователи: {users}")

    # Получение последнего пользователя
    last_user = cred_manager.get_last_saved_user()
    print(f"Последний пользователь: {last_user}")

    # Проверка существования пользователя
    exists = cred_manager.user_exists("player123")
    print(f"Пользователь существует: {exists}")

    # Получение метаданных
    metadata = cred_manager.get_user_metadata("player123")
    print(f"Метаданные: {metadata}")