# Модель данных и работа с JSON
import json
import os
from datetime import datetime
from typing import Dict, List, Optional
from dataclasses import dataclass, asdict, field
import hashlib


@dataclass
class User:
    """Модель пользователя"""
    telegram_id: int
    first_name: str
    username: Optional[str] = None
    is_butterfly_owner: bool = False  # Владелец Бабочкария
    school_completed: bool = False
    total_points: int = 0
    team_id: Optional[int] = None  # ID капитана стаи
    created_at: str = field(default_factory=lambda: datetime.now().isoformat())
    last_activation_date: Optional[str] = None  # Дата последней активации Бабочкария
    invited_friends: List[int] = field(default_factory=list)  # IDs приглашённых друзей
    referred_by: Optional[int] = None  # ID того, кто пригласил
    profile_updated: bool = False


@dataclass
class Team:
    """Модель стаи бабочек"""
    team_id: int  # ID капитана
    team_name: str
    members: List[int] = field(default_factory=list)  # IDs участников
    created_at: str = field(default_factory=lambda: datetime.now().isoformat())
    total_points: int = 0  # Сумма очков всех участников
    
    def get_points(self, users_data: Dict) -> int:
        """Вычислить сумму очков стаи"""
        total = 0
        for member_id in self.members:
            if member_id in users_data:
                total += users_data[member_id].get('total_points', 0)
        return total


@dataclass
class SchoolQuestion:
    """Модель вопроса школы"""
    question_number: int
    title: str
    learning_text: str
    question_text: str
    options: List[str]
    correct_answer: int  # Индекс правильного ответа
    explanation: str


class Database:
    """Класс для работы с базой данных"""
    
    def __init__(self, users_file: str = 'data/users.json'):
        self.users_file = users_file
        self.ensure_database_exists()
    
    def ensure_database_exists(self):
        """Создать директорию и файлы, если их нет"""
        os.makedirs('data', exist_ok=True)
        
        if not os.path.exists(self.users_file):
            self.save_data({})
    
    def load_data(self) -> Dict:
        """Загрузить все данные пользователей"""
        try:
            with open(self.users_file, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            print(f"Ошибка при загрузке данных: {e}")
            return {}
    
    def save_data(self, data: Dict):
        """Сохранить данные пользователей"""
        try:
            with open(self.users_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
        except Exception as e:
            print(f"Ошибка при сохранении данных: {e}")
    
    def get_user(self, telegram_id: int) -> Optional[Dict]:
        """Получить пользователя по ID"""
        data = self.load_data()
        user_id_str = str(telegram_id)
        return data.get(user_id_str)
    
    def user_exists(self, telegram_id: int) -> bool:
        """Проверить, существует ли пользователь"""
        return self.get_user(telegram_id) is not None
    
    def create_user(self, telegram_id: int, first_name: str, 
                   username: Optional[str] = None, 
                   is_butterfly_owner: bool = False,
                   referred_by: Optional[int] = None) -> Dict:
        """Создать нового пользователя"""
        data = self.load_data()
        user_id_str = str(telegram_id)
        
        if user_id_str in data:
            return data[user_id_str]
        
        new_user = {
            'telegram_id': telegram_id,
            'first_name': first_name,
            'username': username,
            'is_butterfly_owner': is_butterfly_owner,
            'school_completed': False,
            'total_points': 0,
            'team_id': None,
            'team_name': None,
            'created_at': datetime.now().isoformat(),
            'last_activation_date': None,
            'invited_friends': [],
            'referred_by': referred_by,
            'profile_updated': False
        }
        
        data[user_id_str] = new_user
        self.save_data(data)
        return new_user
    
    def update_user(self, telegram_id: int, **kwargs):
        """Обновить данные пользователя"""
        data = self.load_data()
        user_id_str = str(telegram_id)
        
        if user_id_str in data:
            data[user_id_str].update(kwargs)
            self.save_data(data)
            return data[user_id_str]
        return None
    
    def add_points(self, telegram_id: int, points: int):
        """Добавить очки пользователю"""
        user = self.get_user(telegram_id)
        if user:
            current_points = user.get('total_points', 0)
            self.update_user(telegram_id, total_points=current_points + points)
    
    def get_team_members(self, team_id: int) -> List[Dict]:
        """Получить всех членов стаи"""
        data = self.load_data()
        members = []
        for user_id_str, user in data.items():
            if user.get('team_id') == team_id:
                members.append(user)
        return members
    
    def get_teams_ranking(self) -> List[Dict]:
        """Получить рейтинг стай (топ-10)"""
        data = self.load_data()
        teams = {}
        
        # Собрать стаи
        for user_id_str, user in data.items():
            if user.get('is_butterfly_owner'):
                team_id = int(user_id_str)
                members = self.get_team_members(team_id)
                
                team_points = sum(m.get('total_points', 0) for m in members)
                team_name = f"Стая {user.get('first_name', 'Аноним')}"
                
                teams[team_id] = {
                    'team_id': team_id,
                    'team_name': team_name,
                    'captain_name': user.get('first_name', 'Аноним'),
                    'members_count': len(members),
                    'total_points': team_points
                }
        
        # Сортировать по очкам
        sorted_teams = sorted(teams.values(), key=lambda x: x['total_points'], reverse=True)
        return sorted_teams[:10]
    
    def get_users_ranking(self) -> List[Dict]:
        """Получить рейтинг пользователей (топ-10)"""
        data = self.load_data()
        users = []
        
        for user_id_str, user in data.items():
            users.append({
                'telegram_id': int(user_id_str),
                'first_name': user.get('first_name', 'Аноним'),
                'total_points': user.get('total_points', 0),
                'team_id': user.get('team_id')
            })
        
        # Сортировать по очкам
        sorted_users = sorted(users, key=lambda x: x['total_points'], reverse=True)
        return sorted_users[:10]
    
    def get_user_ranking_position(self, telegram_id: int) -> int:
        """Получить позицию пользователя в общем рейтинге"""
        ranking = self.get_users_ranking()
        for idx, user in enumerate(ranking, 1):
            if user['telegram_id'] == telegram_id:
                return idx
        
        # Если не в топ-10, считаем полный рейтинг
        data = self.load_data()
        all_users = []
        for user_id_str, user in data.items():
            all_users.append({
                'telegram_id': int(user_id_str),
                'total_points': user.get('total_points', 0)
            })
        
        all_users = sorted(all_users, key=lambda x: x['total_points'], reverse=True)
        for idx, user in enumerate(all_users, 1):
            if user['telegram_id'] == telegram_id:
                return idx
        return len(all_users) + 1
    
    def get_team_ranking_position(self, team_id: int) -> int:
        """Получить позицию стаи в рейтинге"""
        ranking = self.get_teams_ranking()
        for idx, team in enumerate(ranking, 1):
            if team['team_id'] == team_id:
                return idx
        return None
    
    def create_team(self, captain_id: int, team_name: str) -> Dict:
        """Создать новую стаю"""
        user = self.get_user(captain_id)
        if not user:
            return None
        
        # Обновляем пользователя - он становится капитаном
        self.update_user(captain_id, team_id=captain_id, team_name=team_name)
        
        return {
            'team_id': captain_id,
            'team_name': team_name,
            'members': [captain_id],
            'created_at': datetime.now().isoformat()
        }
    
    def get_team(self, team_id: int) -> Optional[Dict]:
        """Получить информацию о стае"""
        captain = self.get_user(team_id)
        if not captain:
            return None
        
        # Капитан должен иметь team_id == team_id (быть капитаном)
        if captain.get('team_id') != team_id:
            return None
        
        members = self.get_team_members(team_id)
        total_points = sum(m.get('total_points', 0) for m in members)
        
        return {
            'team_id': team_id,
            'team_name': captain.get('team_name', f"Стая {captain.get('first_name', 'Аноним')}"),
            'captain_name': captain.get('first_name', 'Аноним'),
            'members': [m['telegram_id'] for m in members],
            'members_count': len(members),
            'total_points': total_points,
            'created_at': captain.get('created_at')
        }
    
    def add_member_to_team(self, team_id: int, user_id: int) -> bool:
        """Добавить пользователя в стаю"""
        user = self.get_user(user_id)
        if not user:
            return False
        
        # Обновляем team_id пользователя
        self.update_user(user_id, team_id=team_id)
        return True
    
    def remove_member_from_team(self, team_id: int, user_id: int) -> bool:
        """Удалить пользователя из стаи"""
        user = self.get_user(user_id)
        if not user or user.get('team_id') != team_id:
            return False
        
        # Обновляем team_id пользователя на None и очищаем team_name
        self.update_user(user_id, team_id=None, team_name=None)
        return True
    
    def update_team_name(self, team_id: int, new_name: str) -> bool:
        """Изменить название стаи"""
        captain = self.get_user(team_id)
        if not captain or captain.get('team_id') != team_id:
            return False
        
        self.update_user(team_id, team_name=new_name)
        return True
    
    def disband_team(self, team_id: int) -> bool:
        """Разойти стаю - удалить всех участников из команды"""
        members = self.get_team_members(team_id)
        
        for member in members:
            self.update_user(member['telegram_id'], team_id=None, team_name=None)
        
        # Капитан тоже выходит
        self.update_user(team_id, team_id=None, team_name=None)
        return True
    
    def mark_school_completed(self, telegram_id: int) -> bool:
        """Отметить школу как пройденную"""
        self.update_user(telegram_id, school_completed=True)
        return True
