
from time import time ,localtime, strftime
import uuid
from fastapi import FastAPI,HTTPException
import pandas as pd
from pydantic import BaseModel
from typing import List, Dict, Any
from .core.model import load_model, predict,update_model_data,get_player_data,model_retarin
from contextlib import asynccontextmanager
import logging
from app.logging_config import setup_logging


class PredictionRequest(BaseModel):
    gw: int

logger = logging.getLogger(__name__)

@asynccontextmanager
async def lifespan(app: FastAPI):
    """
    Lifespan event to load the model when the application starts.
    """
    setup_logging()
    logger.info("Starting FastAPI application and loading model...")
    try:
        model = load_model()
        if not model:
            logger.error("Model loading failed.")
            raise RuntimeError("Failed to load the model on startup.")
    except Exception as e:
        logger.error(f"Error loading model: {e}")
        raise RuntimeError("Application startup failed due to model loading error.") 
    yield
    logger.info("FastAPI application is shutting down...")
    # Here you can add any cleanup code if necessary


app = FastAPI(lifespan=lifespan)

@app.middleware("http")
async def log_requests(request, call_next):
    request_id = str(uuid.uuid4())
    start_time = time()
    logger.info(f"Incoming Request",extra={
        "request_id": request_id,
        "request":{
            "method": request.method,
            "url": str(request.url),
            "client": str(request.client),
        },
        "timestamp": strftime("%Y-%m-%d %H:%M:%S", localtime(start_time)),
        }
        )
    
    response = await call_next(request)
    process_time = time() - start_time  
    logger.info(f"Request Finished", extra={
        "request_id": request_id,
        "response": {
            "status_code": response.status_code,
            "process_time": process_time
        },
    })
    return response
        

@app.get("/")
def read_root():
    return {"message": "Welcome to the FPL Prediction API!"}
@app.post("/predict")
def predict_endpoint(request: PredictionRequest):
    """
    Endpoint to make predictions using the pre-trained model.
    
    :param request: PredictionRequest containing the features for prediction.
    :return: Dictionary with predictions.
    """
    logger.info(f"Get Player XP Predictions for GW : {request.gw}",)
    if not request.gw:
        logger.error("No gameweek provided for prediction.")
        raise HTTPException(status_code=400, detail="No features provided for prediction.")
    
    gw = request.gw
    logger.info(f"Prediction request received for GW {gw}.")
    try:
        predictions = predict(gw)
        logger.info(f"Prediction for GW {gw} completed successfully.")
         # Convert DataFrame to JSON format
         # Ensure the DataFrame is converted to a format suitable for JSON serialization
         # This will return a list of dictionaries, each representing a row in the DataFrame
         # with 'id' and 'prediction' keys.
         # If you want to return the entire DataFrame as a JSON object, you can use `to_json(orient='records')`
         # or any other suitable orientation.
        return predictions.to_dict(orient='records')
    except ValueError as e:
        logger.error(f"Prediction error: {e}")
         # Raise an HTTP exception with a 400 status code and the error message
         # This will return a JSON response with the error details
         # The client can handle this error appropriately
        raise HTTPException(status_code=400, detail=str(e))
    
@app.get("/update")
def update_data():
    logger.info(f"Starting Data Update")
    update_model_data()
    logger.info(f"Data Update completed successfully." )

@app.get("/gw_data/{gw}")
def gw_data(gw : int):
    logger.info(f"Fetching GW {gw} Players Data")
    players_data = get_player_data(gw)
    logger.info(f"Fetching GW {gw} Players Data completed successfully." )
    return players_data

@app.get("/retrain/{gw}")
def retrain(gw : int):
    logger.info(f"Starting Model Retrain")
    model_retarin(gw)
    logger.info(f"Model Retrain completed successfully." )