from __future__ import annotations

import uuid
from datetime import datetime, timedelta, timezone
from typing import Any, Dict, List

from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.orm import Session, joinedload

from app.database import get_db
from app.models.graft_combination import GraftCombination
from app.models.plant import Plant
from app.models.scenario import Scenario, ScenarioResult
from app.models.soil_profile import SoilProfile
from app.schemas.scenario import (
    AnalyzeRequest,
    ScenarioCreate,
    ScenarioResponse,
    ScenarioSummary,
)
from app.services import ai

router = APIRouter(prefix="/api/scenarios", tags=["scenarios"])

# Eager-load everything needed to build a full ScenarioResponse in one query
_full_load = [
    joinedload(Scenario.soil_profile),
    joinedload(Scenario.plant),
    joinedload(Scenario.graft_combination).joinedload(GraftCombination.rootstock),
    joinedload(Scenario.graft_combination).joinedload(GraftCombination.scion),
    joinedload(Scenario.results),
]


def _fetch_soil(db: Session, soil_id: uuid.UUID) -> SoilProfile:
    soil = db.query(SoilProfile).filter(SoilProfile.id == soil_id).first()
    if not soil:
        raise HTTPException(status_code=404, detail="Soil profile not found")
    return soil


def _fetch_graft(db: Session, graft_id: uuid.UUID) -> GraftCombination:
    graft = (
        db.query(GraftCombination)
        .options(
            joinedload(GraftCombination.rootstock),
            joinedload(GraftCombination.scion),
        )
        .filter(GraftCombination.id == graft_id)
        .first()
    )
    if not graft:
        raise HTTPException(status_code=404, detail="Graft combination not found")
    return graft


def _fetch_plant(db: Session, plant_id: uuid.UUID) -> Plant:
    plant = db.query(Plant).filter(Plant.id == plant_id).first()
    if not plant:
        raise HTTPException(status_code=404, detail="Plant not found")
    return plant


def _run_ai(
    payload: AnalyzeRequest,
    soil: SoilProfile,
    plant: Plant | None,
    graft: GraftCombination | None,
) -> Dict[str, Any]:
    return ai.run_analysis(
        soil=soil,
        applied_n=payload.applied_nitrogen,
        applied_p=payload.applied_phosphorus,
        applied_k=payload.applied_potassium,
        irrigation_method=payload.irrigation_method.value,
        irrigation_frequency_days=payload.irrigation_frequency_days,
        season=payload.season.value,
        duration_days=payload.duration_days,
        plant=plant,
        graft=graft,
    )


@router.post("", response_model=ScenarioResponse, status_code=201)
def create_scenario(
    request: Request, payload: ScenarioCreate, db: Session = Depends(get_db)
):
    # Get real IP if behind a proxy
    forwarded = request.headers.get("X-Forwarded-For")
    if forwarded:
        client_ip = forwarded.split(",")[0].strip()
    else:
        client_ip = request.client.host if request.client else "unknown"

    # Rate Limit Check: 5 per 24 hours
    one_day_ago = datetime.now(timezone.utc) - timedelta(days=1)
    recent_count = (
        db.query(Scenario)
        .filter(Scenario.client_ip == client_ip, Scenario.created_at >= one_day_ago)
        .count()
    )

    if recent_count >= 5:
        raise HTTPException(
            status_code=429,
            detail="Daily limit reached. You can create up to 5 scenarios every 24 hours.",
        )

    soil = _fetch_soil(db, payload.soil_profile_id)
    graft = (
        _fetch_graft(db, payload.graft_combination_id)
        if payload.graft_combination_id
        else None
    )
    plant = _fetch_plant(db, payload.plant_id) if payload.plant_id else None

    analysis = _run_ai(payload, soil, plant, graft)

    scenario = Scenario(
        name=payload.name,
        graft_combination_id=payload.graft_combination_id,
        plant_id=payload.plant_id,
        soil_profile_id=payload.soil_profile_id,
        applied_nitrogen=payload.applied_nitrogen,
        applied_phosphorus=payload.applied_phosphorus,
        applied_potassium=payload.applied_potassium,
        irrigation_method=payload.irrigation_method,
        irrigation_frequency_days=payload.irrigation_frequency_days,
        season=payload.season,
        duration_days=payload.duration_days,
        client_ip=client_ip,
    )
    db.add(scenario)
    db.flush()  # get scenario.id without committing

    result = ScenarioResult(
        scenario_id=scenario.id,
        predicted_yield=analysis.get("predicted_yield"),
        survival_probability=analysis.get("survival_probability"),
        risk_factors=analysis.get("risk_factors", []),
        recommended_adjustments=analysis.get("recommended_adjustments", {}),
        growth_timeline=analysis.get("growth_timeline", []),
        confidence_score=analysis.get("confidence_score"),
        ai_analysis=analysis.get("ai_analysis"),
    )
    db.add(result)
    db.commit()

    # Re-fetch with all relations loaded for the response
    return (
        db.query(Scenario)
        .options(*_full_load)
        .filter(Scenario.id == scenario.id)
        .one()
    )


@router.get("", response_model=List[ScenarioSummary])
def list_scenarios(db: Session = Depends(get_db)):
    scenarios = (
        db.query(Scenario)
        .options(joinedload(Scenario.results))
        .order_by(Scenario.created_at.desc())
        .all()
    )
    summaries = []
    for s in scenarios:
        summaries.append(
            ScenarioSummary(
                id=s.id,
                name=s.name,
                season=s.season,
                duration_days=s.duration_days,
                created_at=s.created_at,
                predicted_yield=s.results.predicted_yield if s.results else None,
                survival_probability=s.results.survival_probability if s.results else None,
                confidence_score=s.results.confidence_score if s.results else None,
            )
        )
    return summaries


@router.get("/{scenario_id}", response_model=ScenarioResponse)
def get_scenario(scenario_id: uuid.UUID, db: Session = Depends(get_db)):
    scenario = (
        db.query(Scenario)
        .options(*_full_load)
        .filter(Scenario.id == scenario_id)
        .first()
    )
    if not scenario:
        raise HTTPException(status_code=404, detail="Scenario not found")
    return scenario


@router.post("/analyze", response_model=Dict[str, Any])
def analyze(payload: AnalyzeRequest, db: Session = Depends(get_db)):
    """Ephemeral analysis — runs AI and returns the result without saving."""
    soil = _fetch_soil(db, payload.soil_profile_id)
    graft = _fetch_graft(db, payload.graft_combination_id) if payload.graft_combination_id else None
    plant = _fetch_plant(db, payload.plant_id) if payload.plant_id else None
    return _run_ai(payload, soil, plant, graft)
