#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Модуль для работы с базой данных AI-КАЛОРИЯ бота
"""

import logging
from datetime import datetime, timedelta
from typing import List, Dict, Any, Optional
from sqlalchemy import create_engine, Column, Integer, String, Float, DateTime, Text, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session
from sqlalchemy.exc import SQLAlchemyError

from config import Config

logger = logging.getLogger(__name__)

# Создание базового класса для моделей
Base = declarative_base()

class FoodRecord(Base):
    """Модель записи о еде"""
    __tablename__ = 'food_records'
    
    id = Column(Integer, primary_key=True, autoincrement=True)
    user_id = Column(Integer, nullable=False, index=True)
    username = Column(String(255), nullable=True)
    food_name = Column(String(500), nullable=False)
    amount = Column(Float, nullable=False)
    unit = Column(String(50), nullable=False)
    calories = Column(Float, nullable=False)
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
    raw_text = Column(Text, nullable=True)  # Исходный текст сообщения
    
    def __repr__(self):
        return f"<FoodRecord(id={self.id}, user_id={self.user_id}, food='{self.food_name}', calories={self.calories})>"

class DatabaseManager:
    """Менеджер базы данных"""
    
    def __init__(self):
        self.engine = None
        self.SessionLocal = None
        self._initialized = False
    
    def init_database(self):
        """Инициализация базы данных"""
        try:
            # Создание движка базы данных
            db_config = Config.get_database_config()
            self.engine = create_engine(
                db_config['url'],
                pool_size=db_config['pool_size'],
                max_overflow=db_config['max_overflow'],
                echo=db_config['echo']
            )
            
            # Создание фабрики сессий
            self.SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=self.engine)
            
            # Создание таблиц
            Base.metadata.create_all(bind=self.engine)
            
            self._initialized = True
            logger.info("База данных успешно инициализирована")
            
        except Exception as e:
            logger.error(f"Ошибка при инициализации базы данных: {e}")
            raise
    
    def get_session(self) -> Session:
        """Получение сессии базы данных"""
        if not self._initialized:
            raise RuntimeError("База данных не инициализирована. Вызовите init_database() сначала.")
        
        return self.SessionLocal()
    
    def add_food_record(self, user_id: int, username: str, food_name: str, 
                       amount: float, unit: str, calories: float, raw_text: str = None) -> int:
        """Добавление записи о еде"""
        session = self.get_session()
        try:
            record = FoodRecord(
                user_id=user_id,
                username=username,
                food_name=food_name,
                amount=amount,
                unit=unit,
                calories=calories,
                raw_text=raw_text
            )
            
            session.add(record)
            session.commit()
            
            record_id = record.id
            logger.info(f"Добавлена запись {record_id} для пользователя {user_id}: {food_name}")
            
            return record_id
            
        except SQLAlchemyError as e:
            session.rollback()
            logger.error(f"Ошибка при добавлении записи для пользователя {user_id}: {e}")
            raise
        finally:
            session.close()
    
    def get_user_stats(self, user_id: int) -> Optional[Dict[str, Any]]:
        """Получение статистики пользователя"""
        session = self.get_session()
        try:
            # Общее количество записей
            total_records = session.query(func.count(FoodRecord.id)).filter(
                FoodRecord.user_id == user_id
            ).scalar()
            
            if total_records == 0:
                return None
            
            # Общие калории
            total_calories = session.query(func.sum(FoodRecord.calories)).filter(
                FoodRecord.user_id == user_id
            ).scalar() or 0
            
            # Последняя запись
            last_record = session.query(FoodRecord).filter(
                FoodRecord.user_id == user_id
            ).order_by(FoodRecord.created_at.desc()).first()
            
            # Среднее за день (за последние 30 дней)
            thirty_days_ago = datetime.utcnow() - timedelta(days=30)
            recent_calories = session.query(func.sum(FoodRecord.calories)).filter(
                FoodRecord.user_id == user_id,
                FoodRecord.created_at >= thirty_days_ago
            ).scalar() or 0
            
            avg_calories_per_day = recent_calories / 30 if recent_calories > 0 else 0
            
            # Топ продуктов
            top_foods = session.query(
                FoodRecord.food_name,
                func.count(FoodRecord.id).label('count')
            ).filter(
                FoodRecord.user_id == user_id
            ).group_by(FoodRecord.food_name).order_by(
                func.count(FoodRecord.id).desc()
            ).limit(5).all()
            
            return {
                'total_records': total_records,
                'total_calories': float(total_calories),
                'avg_calories_per_day': avg_calories_per_day,
                'last_record_date': last_record.created_at.strftime('%d.%m.%Y %H:%M') if last_record else None,
                'top_foods': [{'food_name': food.food_name, 'count': food.count} for food in top_foods]
            }
            
        except SQLAlchemyError as e:
            logger.error(f"Ошибка при получении статистики для пользователя {user_id}: {e}")
            raise
        finally:
            session.close()
    
    def get_user_history(self, user_id: int, limit: int = 10) -> List[Dict[str, Any]]:
        """Получение истории записей пользователя"""
        session = self.get_session()
        try:
            records = session.query(FoodRecord).filter(
                FoodRecord.user_id == user_id
            ).order_by(FoodRecord.created_at.desc()).limit(limit).all()
            
            return [
                {
                    'id': record.id,
                    'food_name': record.food_name,
                    'amount': record.amount,
                    'unit': record.unit,
                    'calories': record.calories,
                    'created_at': record.created_at,
                    'raw_text': record.raw_text
                }
                for record in records
            ]
            
        except SQLAlchemyError as e:
            logger.error(f"Ошибка при получении истории для пользователя {user_id}: {e}")
            raise
        finally:
            session.close()
    
    def clear_user_history(self, user_id: int) -> int:
        """Очистка истории пользователя"""
        session = self.get_session()
        try:
            deleted_count = session.query(FoodRecord).filter(
                FoodRecord.user_id == user_id
            ).delete()
            
            session.commit()
            logger.info(f"Удалено {deleted_count} записей для пользователя {user_id}")
            
            return deleted_count
            
        except SQLAlchemyError as e:
            session.rollback()
            logger.error(f"Ошибка при очистке истории для пользователя {user_id}: {e}")
            raise
        finally:
            session.close()
    
    def get_daily_stats(self, user_id: int, date: datetime = None) -> Dict[str, Any]:
        """Получение статистики за день"""
        if date is None:
            date = datetime.utcnow().date()
        
        session = self.get_session()
        try:
            start_date = datetime.combine(date, datetime.min.time())
            end_date = datetime.combine(date, datetime.max.time())
            
            records = session.query(FoodRecord).filter(
                FoodRecord.user_id == user_id,
                FoodRecord.created_at >= start_date,
                FoodRecord.created_at <= end_date
            ).all()
            
            total_calories = sum(record.calories for record in records)
            
            return {
                'date': date.strftime('%d.%m.%Y'),
                'records_count': len(records),
                'total_calories': total_calories,
                'records': [
                    {
                        'food_name': record.food_name,
                        'amount': record.amount,
                        'unit': record.unit,
                        'calories': record.calories,
                        'time': record.created_at.strftime('%H:%M')
                    }
                    for record in records
                ]
            }
            
        except SQLAlchemyError as e:
            logger.error(f"Ошибка при получении дневной статистики для пользователя {user_id}: {e}")
            raise
        finally:
            session.close()
    
    def search_food_records(self, user_id: int, query: str, limit: int = 10) -> List[Dict[str, Any]]:
        """Поиск записей по названию еды"""
        session = self.get_session()
        try:
            records = session.query(FoodRecord).filter(
                FoodRecord.user_id == user_id,
                FoodRecord.food_name.ilike(f'%{query}%')
            ).order_by(FoodRecord.created_at.desc()).limit(limit).all()
            
            return [
                {
                    'id': record.id,
                    'food_name': record.food_name,
                    'amount': record.amount,
                    'unit': record.unit,
                    'calories': record.calories,
                    'created_at': record.created_at
                }
                for record in records
            ]
            
        except SQLAlchemyError as e:
            logger.error(f"Ошибка при поиске записей для пользователя {user_id}: {e}")
            raise
        finally:
            session.close()
    
    def get_user_by_id(self, user_id: int) -> Optional[Dict[str, Any]]:
        """Получение информации о пользователе"""
        session = self.get_session()
        try:
            first_record = session.query(FoodRecord).filter(
                FoodRecord.user_id == user_id
            ).order_by(FoodRecord.created_at.asc()).first()
            
            if not first_record:
                return None
            
            return {
                'user_id': user_id,
                'username': first_record.username,
                'first_record_date': first_record.created_at,
                'total_records': session.query(func.count(FoodRecord.id)).filter(
                    FoodRecord.user_id == user_id
                ).scalar()
            }
            
        except SQLAlchemyError as e:
            logger.error(f"Ошибка при получении информации о пользователе {user_id}: {e}")
            raise
        finally:
            session.close()
    
    def get_user_records_by_date(self, user_id: int, target_date) -> List[Dict[str, Any]]:
        """Получить записи пользователя за конкретную дату"""
        session = self.get_session()
        try:
            # Получаем начало и конец дня
            start_date = datetime.combine(target_date, datetime.min.time())
            end_date = datetime.combine(target_date, datetime.max.time())
            
            records = session.query(FoodRecord).filter(
                FoodRecord.user_id == user_id,
                FoodRecord.created_at >= start_date,
                FoodRecord.created_at <= end_date
            ).order_by(FoodRecord.created_at.desc()).all()
            
            return [
                {
                    'id': record.id,
                    'food_name': record.food_name,
                    'amount': record.amount,
                    'unit': record.unit,
                    'calories': record.calories,
                    'created_at': record.created_at
                }
                for record in records
            ]
            
        except SQLAlchemyError as e:
            logger.error(f"Ошибка при получении записей за дату {target_date} для пользователя {user_id}: {e}")
            return []
        finally:
            session.close()

    def get_user_recent_records(self, user_id: int, limit: int = 10) -> List[Dict[str, Any]]:
        """Получить последние записи пользователя"""
        session = self.get_session()
        try:
            records = session.query(FoodRecord).filter(
                FoodRecord.user_id == user_id
            ).order_by(FoodRecord.created_at.desc()).limit(limit).all()
            
            return [
                {
                    'id': record.id,
                    'food_name': record.food_name,
                    'amount': record.amount,
                    'unit': record.unit,
                    'calories': record.calories,
                    'created_at': record.created_at
                }
                for record in records
            ]
            
        except SQLAlchemyError as e:
            logger.error(f"Ошибка при получении последних записей для пользователя {user_id}: {e}")
            return []
        finally:
            session.close()

    def delete_food_record(self, record_id: int, user_id: int) -> bool:
        """Удалить конкретную запись о еде"""
        session = self.get_session()
        try:
            # Находим запись и проверяем, что она принадлежит пользователю
            record = session.query(FoodRecord).filter(
                FoodRecord.id == record_id,
                FoodRecord.user_id == user_id
            ).first()
            
            if not record:
                logger.warning(f"Запись {record_id} не найдена для пользователя {user_id}")
                return False
            
            # Удаляем запись
            session.delete(record)
            session.commit()
            
            logger.info(f"Запись {record_id} удалена для пользователя {user_id}")
            return True
            
        except SQLAlchemyError as e:
            session.rollback()
            logger.error(f"Ошибка при удалении записи {record_id} для пользователя {user_id}: {e}")
            return False
        finally:
            session.close()

    def get_record_by_id(self, record_id: int, user_id: int) -> Optional[Dict[str, Any]]:
        """Получить запись по ID для конкретного пользователя"""
        session = self.get_session()
        try:
            record = session.query(FoodRecord).filter(
                FoodRecord.id == record_id,
                FoodRecord.user_id == user_id
            ).first()
            
            if not record:
                return None
            
            return {
                'id': record.id,
                'food_name': record.food_name,
                'amount': record.amount,
                'unit': record.unit,
                'calories': record.calories,
                'created_at': record.created_at
            }
            
        except SQLAlchemyError as e:
            logger.error(f"Ошибка при получении записи {record_id} для пользователя {user_id}: {e}")
            return None
        finally:
            session.close()

    def update_record_calories(self, record_id: int, user_id: int, new_calories: float) -> bool:
        """Обновить калории записи"""
        session = self.get_session()
        try:
            # Находим запись и проверяем, что она принадлежит пользователю
            record = session.query(FoodRecord).filter(
                FoodRecord.id == record_id,
                FoodRecord.user_id == user_id
            ).first()
            
            if not record:
                logger.warning(f"Запись {record_id} не найдена для пользователя {user_id}")
                return False
            
            # Обновляем калории
            record.calories = new_calories
            session.commit()
            
            logger.info(f"Калории записи {record_id} обновлены на {new_calories} для пользователя {user_id}")
            return True
            
        except SQLAlchemyError as e:
            session.rollback()
            logger.error(f"Ошибка при обновлении калорий записи {record_id} для пользователя {user_id}: {e}")
            return False
        finally:
            session.close()

    def update_record(self, record_id: int, user_id: int, changes: dict) -> bool:
        """Обновить запись с изменениями"""
        session = self.get_session()
        try:
            # Находим запись и проверяем, что она принадлежит пользователю
            record = session.query(FoodRecord).filter(
                FoodRecord.id == record_id,
                FoodRecord.user_id == user_id
            ).first()
            
            if not record:
                logger.warning(f"Запись {record_id} не найдена для пользователя {user_id}")
                return False
            
            # Обновляем поля, которые есть в changes
            if 'amount' in changes:
                record.amount = changes['amount']
            if 'unit' in changes:
                record.unit = changes['unit']
            if 'calories' in changes:
                record.calories = changes['calories']
            
            session.commit()
            
            logger.info(f"Запись {record_id} обновлена для пользователя {user_id}: {changes}")
            return True
            
        except SQLAlchemyError as e:
            session.rollback()
            logger.error(f"Ошибка при обновлении записи {record_id} для пользователя {user_id}: {e}")
            return False
        finally:
            session.close()

    def get_user_records_paginated(self, user_id: int, page: int = 1, per_page: int = 10) -> Dict[str, Any]:
        """Получение записей пользователя с пагинацией"""
        session = self.get_session()
        try:
            # Подсчитываем общее количество записей
            total_count = session.query(FoodRecord).filter(
                FoodRecord.user_id == user_id
            ).count()
            
            # Вычисляем смещение
            offset = (page - 1) * per_page
            
            # Получаем записи для текущей страницы
            records = session.query(FoodRecord).filter(
                FoodRecord.user_id == user_id
            ).order_by(FoodRecord.created_at.desc()).offset(offset).limit(per_page).all()
            
            # Вычисляем общее количество страниц
            total_pages = (total_count + per_page - 1) // per_page
            
            return {
                'records': [
                    {
                        'id': record.id,
                        'food_name': record.food_name,
                        'amount': record.amount,
                        'unit': record.unit,
                        'calories': record.calories,
                        'created_at': record.created_at
                    }
                    for record in records
                ],
                'pagination': {
                    'current_page': page,
                    'total_pages': total_pages,
                    'total_records': total_count,
                    'per_page': per_page,
                    'has_prev': page > 1,
                    'has_next': page < total_pages
                }
            }
            
        except SQLAlchemyError as e:
            logger.error(f"Ошибка при получении записей с пагинацией для пользователя {user_id}: {e}")
            raise
        finally:
            session.close()

    def close(self):
        """Закрытие соединения с базой данных"""
        if self.engine:
            self.engine.dispose()
            logger.info("Соединение с базой данных закрыто")
