from __future__ import annotations

from dataclasses import replace
from typing import List
from uuid import UUID

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session

from app.database import get_db
from app.models import FamilyProfile, MealPlan, MealPlanItem
from app.schemas.meal_plan_generation import (
    GenerateRequest,
    GenerationDebug,
    GenerationItem,
    GenerationResponse,
    RegenerateRequest,
)
from app.security import require_admin
from app.services.planner.config import DEFAULT
from app.services.planner.generate import generate_meal_plan
from app.services.planner.types import GenerationResult


admin_router = APIRouter(
    prefix="/api/admin/meal-plans",
    tags=["meal-plans-admin"],
    dependencies=[Depends(require_admin)],
)
public_router = APIRouter(prefix="/api/meal-plans", tags=["meal-plans"])


def _to_response(week_start, plan_id, items: List[MealPlanItem], result: GenerationResult) -> GenerationResponse:
    item_payloads: List[GenerationItem] = []
    score_by_recipe = {s.recipe_id: s for s in result.selected}
    for it in items:
        scored = score_by_recipe.get(it.recipe_id)
        item_payloads.append(
            GenerationItem(
                recipe_id=it.recipe_id,
                day_of_week=it.day_of_week,
                estimated_cost=it.estimated_cost or 0,
                score=scored.score if scored else 0.0,
                components={k: float(v) for k, v in (scored.components.items() if scored else [])},
            )
        )
    return GenerationResponse(
        meal_plan_id=plan_id,
        week_start_date=week_start,
        items=item_payloads,
        debug=GenerationDebug(
            feasible_count=result.feasible_count,
            rejected_summary=result.rejected_summary,
            set_score=result.set_score,
        ),
    )


@admin_router.post("/generate", response_model=GenerationResponse, status_code=status.HTTP_201_CREATED)
def generate(payload: GenerateRequest, db: Session = Depends(get_db)) -> GenerationResponse:
    family = db.query(FamilyProfile).filter(FamilyProfile.id == payload.family_profile_id).first()
    if family is None:
        raise HTTPException(status_code=404, detail="family_profile not found")
    try:
        result = generate_meal_plan(
            db,
            family_id=payload.family_profile_id,
            week_start_date=payload.week_start_date,
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    items = (
        db.query(MealPlanItem)
        .filter(MealPlanItem.meal_plan_id == result.meal_plan_id)
        .order_by(MealPlanItem.day_of_week)
        .all()
    )
    return _to_response(payload.week_start_date, result.meal_plan_id, items, result)


@admin_router.post("/regenerate", response_model=GenerationResponse, status_code=status.HTTP_201_CREATED)
def regenerate(payload: RegenerateRequest, db: Session = Depends(get_db)) -> GenerationResponse:
    family = db.query(FamilyProfile).filter(FamilyProfile.id == payload.family_profile_id).first()
    if family is None:
        raise HTTPException(status_code=404, detail="family_profile not found")

    config = DEFAULT
    if payload.relax_time_max_minutes is not None:
        config = replace(config, max_total_minutes=payload.relax_time_max_minutes)
    if payload.relax_calorie_pct is not None:
        config = replace(config, calorie_tolerance_pct=payload.relax_calorie_pct)
    if payload.relax_max_meal_cost is not None:
        config = replace(config, max_meal_cost=payload.relax_max_meal_cost)

    db.query(MealPlan).filter(
        MealPlan.family_profile_id == payload.family_profile_id,
        MealPlan.week_start_date == payload.week_start_date,
    ).delete(synchronize_session=False)
    db.commit()

    result = generate_meal_plan(
        db,
        family_id=payload.family_profile_id,
        week_start_date=payload.week_start_date,
        config=config,
        exclude_recipe_ids=set(payload.exclude_recipe_ids) if payload.exclude_recipe_ids else None,
    )
    items = (
        db.query(MealPlanItem)
        .filter(MealPlanItem.meal_plan_id == result.meal_plan_id)
        .order_by(MealPlanItem.day_of_week)
        .all()
    )
    return _to_response(payload.week_start_date, result.meal_plan_id, items, result)


@public_router.get("/{plan_id}", response_model=GenerationResponse)
def get_plan(plan_id: UUID, db: Session = Depends(get_db)) -> GenerationResponse:
    plan = db.query(MealPlan).filter(MealPlan.id == plan_id).first()
    if plan is None:
        raise HTTPException(status_code=404, detail="meal_plan not found")
    items = (
        db.query(MealPlanItem)
        .filter(MealPlanItem.meal_plan_id == plan.id)
        .order_by(MealPlanItem.day_of_week)
        .all()
    )
    # Read-after-create: scores now stored on MealPlanItem
    return GenerationResponse(
        meal_plan_id=plan.id,
        week_start_date=plan.week_start_date,
        items=[
            GenerationItem(
                recipe_id=it.recipe_id,
                day_of_week=it.day_of_week,
                estimated_cost=it.estimated_cost or 0,
                score=it.score or 0.0,
                components={k: float(v) for k, v in (it.components.items() if it.components else [])},
            )
            for it in items
        ],
        debug=GenerationDebug(feasible_count=0, rejected_summary={}, set_score=0.0),
    )
