from fastapi import FastAPI, APIRouter, HTTPException
from dotenv import load_dotenv
from starlette.middleware.cors import CORSMiddleware
from motor.motor_asyncio import AsyncIOMotorClient
import os
import json
import logging
import re
import uuid
from pathlib import Path
from pydantic import BaseModel, Field, ConfigDict
from typing import List, Optional
from datetime import datetime, timezone

ROOT_DIR = Path(__file__).parent
load_dotenv(ROOT_DIR / '.env')

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

# MongoDB connection
mongo_url = os.environ['MONGO_URL']
client = AsyncIOMotorClient(mongo_url)
db = client[os.environ['DB_NAME']]

EMERGENT_LLM_KEY = os.environ.get("EMERGENT_LLM_KEY")

app = FastAPI(title="ASEAN Bakery API")
api_router = APIRouter(prefix="/api")


# -------- Models --------
class StatusCheck(BaseModel):
    model_config = ConfigDict(extra="ignore")
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    client_name: str
    timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))


class StatusCheckCreate(BaseModel):
    client_name: str


class PriceEstimateRequest(BaseModel):
    cake_type: str
    size: str
    flavor: Optional[str] = ""
    extras: Optional[str] = ""


class PriceEstimateResponse(BaseModel):
    id: str
    cake_type: str
    size: str
    flavor: Optional[str] = ""
    extras: Optional[str] = ""
    price_low: float
    price_high: float
    currency: str = "USD"
    reasoning: str
    timestamp: datetime


# -------- Routes --------
@api_router.get("/")
async def root():
    return {"message": "ASEAN Bakery API is running"}


@api_router.post("/status", response_model=StatusCheck)
async def create_status_check(input: StatusCheckCreate):
    status_obj = StatusCheck(**input.model_dump())
    doc = status_obj.model_dump()
    doc['timestamp'] = doc['timestamp'].isoformat()
    await db.status_checks.insert_one(doc)
    return status_obj


@api_router.get("/status", response_model=List[StatusCheck])
async def get_status_checks():
    status_checks = await db.status_checks.find({}, {"_id": 0}).to_list(1000)
    for check in status_checks:
        if isinstance(check.get('timestamp'), str):
            check['timestamp'] = datetime.fromisoformat(check['timestamp'])
    return status_checks


def _parse_price_json(text: str):
    """Extract first JSON object from model output."""
    m = re.search(r"\{[\s\S]*\}", text)
    if not m:
        return None
    try:
        return json.loads(m.group(0))
    except Exception:
        return None


@api_router.post("/estimate-price", response_model=PriceEstimateResponse)
async def estimate_price(req: PriceEstimateRequest):
    if not EMERGENT_LLM_KEY:
        raise HTTPException(status_code=500, detail="LLM key not configured")

    from emergentintegrations.llm.chat import LlmChat, UserMessage

    session_id = f"price-{uuid.uuid4()}"
    system_message = (
        "You are a pricing assistant for a homemade bakery (ASEAN Bakery & Cake) "
        "operating in Colorado, USA. You estimate fair retail prices in USD based on "
        "real-world Colorado bakery market rates for homemade preorder items. "
        "Always return STRICT JSON only — no prose, no markdown — with keys: "
        "price_low (number, USD), price_high (number, USD), reasoning (1-2 short sentences). "
        "Keep prices realistic for a home-based bakery (NOT high-end patisserie). "
        "Typical ranges: small pudding cups $4-$8 each, brownies box $15-$30, "
        "small cake (6-8 servings) $25-$45, medium cake (10-14 servings) $45-$75, "
        "large cake (16-20 servings) $75-$120, macaroni schotel pan $25-$45, "
        "pastel (12pcs) $12-$22."
    )

    chat = LlmChat()

    user_text = (
        f"Estimate the preorder price for this homemade bakery item in Colorado, USA.\n"
        f"- Item type: {req.cake_type}\n"
        f"- Size / servings: {req.size}\n"
        f"- Flavor: {req.flavor or 'standard'}\n"
        f"- Extras / decorations: {req.extras or 'none'}\n\n"
        f"Respond with STRICT JSON only: "
        f'{{"price_low": <number>, "price_high": <number>, "reasoning": "<short>"}}'
    )

    try:
        response_text = await chat.send_message(UserMessage(text=user_text))
    except Exception as e:
        logger.exception("LLM call failed")
        raise HTTPException(status_code=502, detail=f"AI service error: {e}")

    parsed = _parse_price_json(response_text or "")
    if not parsed or "price_low" not in parsed or "price_high" not in parsed:
        parsed = {
            "price_low": 25.0,
            "price_high": 45.0,
            "reasoning": "Estimated using typical Colorado homemade bakery rates (fallback).",
        }

    try:
        price_low = float(parsed["price_low"])
        price_high = float(parsed["price_high"])
    except Exception:
        price_low, price_high = 25.0, 45.0

    if price_high < price_low:
        price_low, price_high = price_high, price_low

    result = PriceEstimateResponse(
        id=str(uuid.uuid4()),
        cake_type=req.cake_type,
        size=req.size,
        flavor=req.flavor or "",
        extras=req.extras or "",
        price_low=round(price_low, 2),
        price_high=round(price_high, 2),
        currency="USD",
        reasoning=str(parsed.get("reasoning", ""))[:400],
        timestamp=datetime.now(timezone.utc),
    )

    doc = result.model_dump()
    doc["timestamp"] = doc["timestamp"].isoformat()
    try:
        await db.price_estimates.insert_one(doc)
    except Exception:
        logger.warning("Failed to persist price estimate", exc_info=True)

    return result


app.include_router(api_router)

app.add_middleware(
    CORSMiddleware,
    allow_credentials=True,
    allow_origins=os.environ.get('CORS_ORIGINS', '*').split(','),
    allow_methods=["*"],
    allow_headers=["*"],
)


@app.on_event("shutdown")
async def shutdown_db_client():
    client.close()
