from datetime import date, datetime, timedelta
from typing import Dict, List, Optional, Any

from fastapi import Depends, FastAPI, HTTPException, Query, Path, status
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy import and_, desc, select, func, text
from sqlalchemy.ext.asyncio import AsyncSession

from services.nutrition_service.models import (
    FoodItem, UserNutritionEntry, WaterIntake, 
    UserActivityEntry, NutritionGoal
)
from services.nutrition_service.schemas import (
    FoodItemCreate, FoodItemResponse, UserNutritionEntryCreate, 
    UserNutritionEntryResponse, WaterIntakeCreate, WaterIntakeResponse,
    UserActivityEntryCreate, UserActivityEntryResponse,
    NutritionGoalCreate, NutritionGoalResponse,
    FoodSearchQuery, FoodDetailsQuery, DailyNutritionSummary
)
from services.nutrition_service.fatsecret_client import FatSecretClient
from shared.auth import get_current_user_from_token
from shared.config import settings
from shared.database import get_db

app = FastAPI(title="Nutrition Service", version="1.0.0")

# CORS middleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.CORS_ORIGINS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Создаем клиент FatSecret
fatsecret_client = FatSecretClient()


@app.get("/health")
async def health_check():
    """Health check endpoint"""
    return {"status": "healthy", "service": "nutrition_service"}


# Эндпоинты для работы с API FatSecret
@app.post("/api/v1/nutrition/search", response_model=List[FoodItemResponse])
async def search_foods(
    search_query: FoodSearchQuery,
    user_data: dict = Depends(get_current_user_from_token),
    db: AsyncSession = Depends(get_db)
):
    """Поиск продуктов питания по запросу в FatSecret API"""
    try:
        # Вызов API FatSecret для поиска продуктов
        search_results = await fatsecret_client.search_foods(
            search_query.query, 
            search_query.page_number, 
            search_query.max_results
        )
        
        # Обработка результатов поиска
        foods = []
        if 'foods' in search_results and 'food' in search_results['foods']:
            food_list = search_results['foods']['food']
            # Если результат всего один, API возвращает словарь вместо списка
            if isinstance(food_list, dict):
                food_list = [food_list]
                
            for food in food_list:
                # Получение деталей о продукте
                food_details = await fatsecret_client.get_food_details(food['food_id'])
                parsed_food = await fatsecret_client.parse_food_data(food_details)
                
                # Проверяем, существует ли продукт в базе данных
                query = select(FoodItem).where(FoodItem.fatsecret_id == parsed_food['fatsecret_id'])
                result = await db.execute(query)
                db_food = result.scalars().first()
                
                # Если продукт не существует, сохраняем его
                if not db_food:
                    db_food = FoodItem(**parsed_food)
                    db.add(db_food)
                    await db.commit()
                    await db.refresh(db_food)
                
                foods.append(FoodItemResponse.model_validate(db_food))
        
        return foods
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error searching foods: {str(e)}"
        )


@app.get("/api/v1/nutrition/food/{food_id}", response_model=FoodItemResponse)
async def get_food_details(
    food_id: int = Path(..., description="ID продукта в базе данных"),
    user_data: dict = Depends(get_current_user_from_token),
    db: AsyncSession = Depends(get_db)
):
    """Получение детальной информации о продукте по ID из базы данных"""
    query = select(FoodItem).where(FoodItem.id == food_id)
    result = await db.execute(query)
    food = result.scalars().first()
    
    if not food:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Food item not found"
        )
    
    return FoodItemResponse.model_validate(food)


@app.get("/api/v1/nutrition/fatsecret/{fatsecret_id}", response_model=FoodItemResponse)
async def get_food_by_fatsecret_id(
    fatsecret_id: str = Path(..., description="ID продукта в FatSecret"),
    user_data: dict = Depends(get_current_user_from_token),
    db: AsyncSession = Depends(get_db)
):
    """Получение детальной информации о продукте по FatSecret ID"""
    # Проверяем, есть ли продукт в нашей базе данных
    query = select(FoodItem).where(FoodItem.fatsecret_id == fatsecret_id)
    result = await db.execute(query)
    food = result.scalars().first()
    
    # Если продукт не найден в базе, запрашиваем его с FatSecret API
    if not food:
        try:
            food_details = await fatsecret_client.get_food_details(fatsecret_id)
            parsed_food = await fatsecret_client.parse_food_data(food_details)
            
            food = FoodItem(**parsed_food)
            db.add(food)
            await db.commit()
            await db.refresh(food)
        except Exception as e:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"Error fetching food details: {str(e)}"
            )
    
    return FoodItemResponse.model_validate(food)


# Эндпоинты для работы с записями питания пользователя
@app.post("/api/v1/nutrition/entries", response_model=UserNutritionEntryResponse)
async def create_nutrition_entry(
    entry_data: UserNutritionEntryCreate,
    user_data: dict = Depends(get_current_user_from_token),
    db: AsyncSession = Depends(get_db)
):
    """Создание новой записи о питании пользователя"""
    # Получаем ID пользователя из токена
    user_id = user_data["user_id"]
    
    # Если указан ID продукта, проверяем его наличие
    food_item = None
    if entry_data.food_item_id:
        query = select(FoodItem).where(FoodItem.id == entry_data.food_item_id)
        result = await db.execute(query)
        food_item = result.scalars().first()
        
        if not food_item:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="Food item not found"
            )
    
    # Создаем данные для записи
    nutrition_data = entry_data.model_dump(exclude={"food_item_id"})
    nutrition_entry = UserNutritionEntry(**nutrition_data, user_id=user_id)
    
    if food_item:
        nutrition_entry.food_item_id = food_item.id
        
        # Если питательные данные не указаны, рассчитываем их на основе продукта
        if not entry_data.calories and food_item.calories:
            nutrition_entry.calories = food_item.calories * entry_data.quantity
        if not entry_data.protein_grams and food_item.protein_grams:
            nutrition_entry.protein_grams = food_item.protein_grams * entry_data.quantity
        if not entry_data.fat_grams and food_item.fat_grams:
            nutrition_entry.fat_grams = food_item.fat_grams * entry_data.quantity
        if not entry_data.carbs_grams and food_item.carbs_grams:
            nutrition_entry.carbs_grams = food_item.carbs_grams * entry_data.quantity
    
    db.add(nutrition_entry)
    await db.commit()
    await db.refresh(nutrition_entry)
    
    # Преобразуем типы для Pydantic validation
    response_data = {
        'id': nutrition_entry.id,
        'uuid': str(nutrition_entry.uuid),
        'user_id': nutrition_entry.user_id,
        'entry_date': nutrition_entry.entry_date,
        'meal_type': nutrition_entry.meal_type,
        'food_item_id': nutrition_entry.food_item_id,
        'custom_food_name': nutrition_entry.custom_food_name,
        'quantity': nutrition_entry.quantity,
        'unit': nutrition_entry.unit,
        'calories': nutrition_entry.calories,
        'protein_grams': nutrition_entry.protein_grams,
        'fat_grams': nutrition_entry.fat_grams,
        'carbs_grams': nutrition_entry.carbs_grams,
        'notes': nutrition_entry.notes,
        'created_at': nutrition_entry.created_at.isoformat() if hasattr(nutrition_entry.created_at, 'isoformat') else str(nutrition_entry.created_at),
        'updated_at': nutrition_entry.updated_at.isoformat() if hasattr(nutrition_entry.updated_at, 'isoformat') else str(nutrition_entry.updated_at),
    }
    
    return UserNutritionEntryResponse(**response_data)


@app.get("/api/v1/nutrition/entries", response_model=List[UserNutritionEntryResponse])
async def get_user_nutrition_entries(
    start_date: date = Query(..., description="Начальная дата для выборки"),
    end_date: date = Query(..., description="Конечная дата для выборки"),
    user_data: dict = Depends(get_current_user_from_token),
    db: AsyncSession = Depends(get_db)
):
    """Получение записей о питании пользователя за указанный период"""
    user_id = user_data["user_id"]
    
    query = (
        select(UserNutritionEntry)
        .where(
            and_(
                UserNutritionEntry.user_id == user_id,
                UserNutritionEntry.entry_date >= start_date,
                UserNutritionEntry.entry_date <= end_date
            )
        )
        .order_by(UserNutritionEntry.entry_date, UserNutritionEntry.meal_type)
    )
    
    result = await db.execute(query)
    entries = result.scalars().all()
    
    return [UserNutritionEntryResponse.model_validate(entry) for entry in entries]


# Эндпоинты для работы с записями о потреблении воды
@app.post("/api/v1/nutrition/water", response_model=WaterIntakeResponse)
async def create_water_intake(
    intake_data: WaterIntakeCreate,
    user_data: dict = Depends(get_current_user_from_token),
    db: AsyncSession = Depends(get_db)
):
    """Создание новой записи о потреблении воды"""
    user_id = user_data["user_id"]
    
    water_intake = WaterIntake(**intake_data.model_dump(), user_id=user_id)
    db.add(water_intake)
    await db.commit()
    await db.refresh(water_intake)
    
    return WaterIntakeResponse.model_validate(water_intake)


@app.get("/api/v1/nutrition/water", response_model=List[WaterIntakeResponse])
async def get_user_water_intake(
    start_date: date = Query(..., description="Начальная дата для выборки"),
    end_date: date = Query(..., description="Конечная дата для выборки"),
    user_data: dict = Depends(get_current_user_from_token),
    db: AsyncSession = Depends(get_db)
):
    """Получение записей о потреблении воды за указанный период"""
    user_id = user_data["user_id"]
    
    query = (
        select(WaterIntake)
        .where(
            and_(
                WaterIntake.user_id == user_id,
                WaterIntake.entry_date >= start_date,
                WaterIntake.entry_date <= end_date
            )
        )
        .order_by(WaterIntake.entry_date, WaterIntake.entry_time)
    )
    
    result = await db.execute(query)
    entries = result.scalars().all()
    
    return [WaterIntakeResponse.model_validate(entry) for entry in entries]


# Эндпоинты для работы с записями о физической активности
@app.post("/api/v1/nutrition/activity", response_model=UserActivityEntryResponse)
async def create_activity_entry(
    activity_data: UserActivityEntryCreate,
    user_data: dict = Depends(get_current_user_from_token),
    db: AsyncSession = Depends(get_db)
):
    """Создание новой записи о физической активности"""
    user_id = user_data["user_id"]
    
    # Если не указаны сожженные калории, рассчитываем примерно
    if not activity_data.calories_burned:
        # Простой расчет на основе типа активности и продолжительности
        # Точный расчет требует больше параметров (вес, рост, возраст, пол)
        activity_intensity = {
            "walking": 5,  # ккал/мин
            "running": 10,
            "cycling": 8,
            "swimming": 9,
            "yoga": 4,
            "weight_training": 6,
            "hiit": 12,
            "pilates": 5,
        }
        
        activity_type = activity_data.activity_type.lower()
        intensity = activity_intensity.get(activity_type, 5)  # По умолчанию 5 ккал/мин
        
        # Увеличиваем интенсивность в зависимости от указанной интенсивности
        if activity_data.intensity == "high":
            intensity *= 1.5
        elif activity_data.intensity == "low":
            intensity *= 0.8
        
        calories_burned = intensity * activity_data.duration_minutes
        activity_data.calories_burned = round(calories_burned, 1)
    
    activity_entry = UserActivityEntry(**activity_data.model_dump(), user_id=user_id)
    db.add(activity_entry)
    await db.commit()
    await db.refresh(activity_entry)
    
    return UserActivityEntryResponse.model_validate(activity_entry)


@app.get("/api/v1/nutrition/activity", response_model=List[UserActivityEntryResponse])
async def get_user_activities(
    start_date: date = Query(..., description="Начальная дата для выборки"),
    end_date: date = Query(..., description="Конечная дата для выборки"),
    user_data: dict = Depends(get_current_user_from_token),
    db: AsyncSession = Depends(get_db)
):
    """Получение записей о физической активности за указанный период"""
    user_id = user_data["user_id"]
    
    query = (
        select(UserActivityEntry)
        .where(
            and_(
                UserActivityEntry.user_id == user_id,
                UserActivityEntry.entry_date >= start_date,
                UserActivityEntry.entry_date <= end_date
            )
        )
        .order_by(UserActivityEntry.entry_date, UserActivityEntry.created_at)
    )
    
    result = await db.execute(query)
    entries = result.scalars().all()
    
    return [UserActivityEntryResponse.model_validate(entry) for entry in entries]


# Эндпоинты для работы с целями питания
@app.post("/api/v1/nutrition/goals", response_model=NutritionGoalResponse)
async def create_or_update_nutrition_goals(
    goal_data: NutritionGoalCreate,
    user_data: dict = Depends(get_current_user_from_token),
    db: AsyncSession = Depends(get_db)
):
    """Создание или обновление целей по питанию и активности"""
    user_id = user_data["user_id"]
    
    # Проверяем, существуют ли уже цели для пользователя
    query = select(NutritionGoal).where(NutritionGoal.user_id == user_id)
    result = await db.execute(query)
    existing_goal = result.scalars().first()
    
    if existing_goal:
        # Обновляем существующую цель
        for key, value in goal_data.model_dump(exclude_unset=True).items():
            setattr(existing_goal, key, value)
        await db.commit()
        await db.refresh(existing_goal)
        return NutritionGoalResponse.model_validate(existing_goal)
    else:
        # Создаем новую цель
        new_goal = NutritionGoal(**goal_data.model_dump(), user_id=user_id)
        db.add(new_goal)
        await db.commit()
        await db.refresh(new_goal)
        return NutritionGoalResponse.model_validate(new_goal)


@app.get("/api/v1/nutrition/goals", response_model=NutritionGoalResponse)
async def get_nutrition_goals(
    user_data: dict = Depends(get_current_user_from_token),
    db: AsyncSession = Depends(get_db)
):
    """Получение целей пользователя по питанию и активности"""
    user_id = user_data["user_id"]
    
    query = select(NutritionGoal).where(NutritionGoal.user_id == user_id)
    result = await db.execute(query)
    goal = result.scalars().first()
    
    if not goal:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Nutrition goals not found for this user"
        )
    
    return NutritionGoalResponse.model_validate(goal)


# Сводные отчеты
@app.get("/api/v1/nutrition/summary/daily", response_model=DailyNutritionSummary)
async def get_daily_nutrition_summary(
    target_date: date = Query(..., description="Дата для получения сводки"),
    user_data: dict = Depends(get_current_user_from_token),
    db: AsyncSession = Depends(get_db)
):
    """Получение дневной сводки по питанию, потреблению воды и физической активности"""
    user_id = user_data["user_id"]
    
    # Запрос записей о питании
    meals_query = select(UserNutritionEntry).where(
        and_(
            UserNutritionEntry.user_id == user_id,
            UserNutritionEntry.entry_date == target_date
        )
    ).order_by(UserNutritionEntry.meal_type)
    
    meals_result = await db.execute(meals_query)
    meals = meals_result.scalars().all()
    
    # Запрос записей о воде
    water_query = select(WaterIntake).where(
        and_(
            WaterIntake.user_id == user_id,
            WaterIntake.entry_date == target_date
        )
    ).order_by(WaterIntake.entry_time)
    
    water_result = await db.execute(water_query)
    water_entries = water_result.scalars().all()
    
    # Запрос записей об активности
    activity_query = select(UserActivityEntry).where(
        and_(
            UserActivityEntry.user_id == user_id,
            UserActivityEntry.entry_date == target_date
        )
    ).order_by(UserActivityEntry.created_at)
    
    activity_result = await db.execute(activity_query)
    activity_entries = activity_result.scalars().all()
    
    # Расчет суммарных значений
    total_calories = sum(meal.calories or 0 for meal in meals)
    total_protein = sum(meal.protein_grams or 0 for meal in meals)
    total_fat = sum(meal.fat_grams or 0 for meal in meals)
    total_carbs = sum(meal.carbs_grams or 0 for meal in meals)
    total_water = sum(water.amount_ml for water in water_entries)
    total_activity = sum(activity.duration_minutes for activity in activity_entries)
    calories_burned = sum(activity.calories_burned or 0 for activity in activity_entries)
    
    # Формирование ответа
    summary = DailyNutritionSummary(
        date=target_date,
        total_calories=total_calories,
        total_protein_grams=total_protein,
        total_fat_grams=total_fat,
        total_carbs_grams=total_carbs,
        total_water_ml=total_water,
        total_activity_minutes=total_activity,
        estimated_calories_burned=calories_burned,
        meals=[UserNutritionEntryResponse.model_validate(meal) for meal in meals],
        water_entries=[WaterIntakeResponse.model_validate(water) for water in water_entries],
        activity_entries=[UserActivityEntryResponse.model_validate(activity) for activity in activity_entries]
    )
    
    return summary