"""Faisal Car Bazar - FastAPI backend."""
import os
import uuid
import logging
from datetime import datetime, timezone
from pathlib import Path
from typing import List, Optional

from fastapi import (
    FastAPI,
    APIRouter,
    HTTPException,
    Depends,
    UploadFile,
    File,
    Query,
    Header,
    Response,
    status,
)
from dotenv import load_dotenv
from starlette.middleware.cors import CORSMiddleware
from motor.motor_asyncio import AsyncIOMotorClient
from pydantic import BaseModel, Field, ConfigDict, EmailStr

from auth import (
    hash_password,
    verify_password,
    create_access_token,
    require_admin,
)
from storage import init_storage, put_object, get_object, get_content_type, APP_NAME
from seed_data import cars_with_ids, gallery_with_ids, SAMPLE_OWNER

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

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

app = FastAPI(title="Faisal Car Bazar API")
api_router = APIRouter(prefix="/api")

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


def now_iso() -> str:
    return datetime.now(timezone.utc).isoformat()


# ---------- Pydantic models ----------
class CarBase(BaseModel):
    name: str
    brand: str
    model: str
    year: int
    price: float
    kilometers: int
    fuel_type: str
    transmission: str
    location: str = "Nagpur"
    color: Optional[str] = ""
    description: Optional[str] = ""
    images: List[str] = Field(default_factory=list)
    status: str = "available"  # available | sold
    featured: bool = False


class CarCreate(CarBase):
    pass


class CarUpdate(BaseModel):
    model_config = ConfigDict(extra="ignore")
    name: Optional[str] = None
    brand: Optional[str] = None
    model: Optional[str] = None
    year: Optional[int] = None
    price: Optional[float] = None
    kilometers: Optional[int] = None
    fuel_type: Optional[str] = None
    transmission: Optional[str] = None
    location: Optional[str] = None
    color: Optional[str] = None
    description: Optional[str] = None
    images: Optional[List[str]] = None
    status: Optional[str] = None
    featured: Optional[bool] = None


class Car(CarBase):
    id: str
    created_at: str
    updated_at: str


class LoginRequest(BaseModel):
    email: EmailStr
    password: str


class LoginResponse(BaseModel):
    access_token: str
    token_type: str = "bearer"
    email: str


class SellSubmission(BaseModel):
    name: str
    mobile: str
    brand: str
    model: str
    year: int
    kilometers: int
    expected_price: float
    message: Optional[str] = ""
    images: List[str] = Field(default_factory=list)


class ContactSubmission(BaseModel):
    name: str
    phone: str
    message: str


class NewsletterSub(BaseModel):
    email: EmailStr


class GalleryItem(BaseModel):
    id: str
    title: str
    image_url: str
    category: str = "showroom"  # showroom | delivery
    created_at: str


class GalleryItemCreate(BaseModel):
    title: str
    image_url: str
    category: str = "showroom"


class Owner(BaseModel):
    name: str
    title: str
    bio: str
    photo_url: str
    years_experience: int
    cars_delivered: int
    happy_customers: int


# ---------- Startup: seed admin, sample cars, gallery, owner ----------
@app.on_event("startup")
async def startup():
    # Storage
    try:
        init_storage()
    except Exception as e:
        logger.error(f"Storage init failed: {e}")

    # Seed admin
    admin_email = os.environ.get("ADMIN_EMAIL", "admin@faisalcarbazar.com")
    admin_password = os.environ.get("ADMIN_PASSWORD", "Admin@123")
    existing = await db.admins.find_one({"email": admin_email})
    if not existing:
        await db.admins.insert_one(
            {
                "id": str(uuid.uuid4()),
                "email": admin_email,
                "password_hash": hash_password(admin_password),
                "created_at": now_iso(),
            }
        )
        logger.info(f"Admin seeded: {admin_email}")

    # Seed cars if empty
    count = await db.cars.count_documents({})
    if count == 0:
        await db.cars.insert_many(cars_with_ids())
        logger.info("Sample cars seeded")

    # Seed gallery
    g_count = await db.gallery.count_documents({})
    if g_count == 0:
        await db.gallery.insert_many(gallery_with_ids())
        logger.info("Sample gallery seeded")

    # Seed owner
    o = await db.owner.find_one({"_id": "owner_profile"})
    if not o:
        await db.owner.insert_one({"_id": "owner_profile", **SAMPLE_OWNER, "updated_at": now_iso()})
        logger.info("Owner profile seeded")


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


# ---------- Health ----------
@api_router.get("/")
async def root():
    return {"message": "Faisal Car Bazar API", "status": "ok"}


# ---------- Auth ----------
@api_router.post("/auth/login", response_model=LoginResponse)
async def login(body: LoginRequest):
    admin = await db.admins.find_one({"email": body.email})
    if not admin or not verify_password(body.password, admin["password_hash"]):
        raise HTTPException(status_code=401, detail="Invalid email or password")
    token = create_access_token(admin["email"])
    return LoginResponse(access_token=token, email=admin["email"])


@api_router.get("/auth/me")
async def me(subject: str = Depends(require_admin)):
    return {"email": subject}


# ---------- Cars (public) ----------
def _serialize_car(doc: dict) -> dict:
    doc.pop("_id", None)
    return doc


@api_router.get("/cars")
async def list_cars(
    brand: Optional[str] = None,
    fuel_type: Optional[str] = None,
    transmission: Optional[str] = None,
    year_min: Optional[int] = None,
    year_max: Optional[int] = None,
    price_min: Optional[float] = None,
    price_max: Optional[float] = None,
    search: Optional[str] = None,
    status_filter: Optional[str] = Query(None, alias="status"),
    featured: Optional[bool] = None,
    sort: str = "newest",  # newest | price_asc | price_desc | year_desc | km_asc
    page: int = 1,
    page_size: int = 15,
):
    query: dict = {}
    if brand:
        query["brand"] = {"$regex": f"^{brand}$", "$options": "i"}
    if fuel_type:
        query["fuel_type"] = fuel_type
    if transmission:
        query["transmission"] = transmission
    if status_filter:
        query["status"] = status_filter
    if featured is not None:
        query["featured"] = featured
    if year_min is not None or year_max is not None:
        q = {}
        if year_min is not None:
            q["$gte"] = year_min
        if year_max is not None:
            q["$lte"] = year_max
        query["year"] = q
    if price_min is not None or price_max is not None:
        q = {}
        if price_min is not None:
            q["$gte"] = price_min
        if price_max is not None:
            q["$lte"] = price_max
        query["price"] = q
    if search:
        query["$or"] = [
            {"name": {"$regex": search, "$options": "i"}},
            {"brand": {"$regex": search, "$options": "i"}},
            {"model": {"$regex": search, "$options": "i"}},
        ]

    sort_map = {
        "newest": [("created_at", -1)],
        "price_asc": [("price", 1)],
        "price_desc": [("price", -1)],
        "year_desc": [("year", -1)],
        "km_asc": [("kilometers", 1)],
    }
    sort_spec = sort_map.get(sort, sort_map["newest"])

    total = await db.cars.count_documents(query)
    page = max(1, page)
    page_size = max(1, min(page_size, 50))
    skip = (page - 1) * page_size

    cursor = db.cars.find(query, {"_id": 0}).sort(sort_spec).skip(skip).limit(page_size)
    items = [_serialize_car(d) async for d in cursor]

    return {
        "items": items,
        "total": total,
        "page": page,
        "page_size": page_size,
        "total_pages": (total + page_size - 1) // page_size if page_size else 1,
    }


@api_router.get("/cars/filters/options")
async def filter_options():
    brands = await db.cars.distinct("brand")
    fuels = await db.cars.distinct("fuel_type")
    transmissions = await db.cars.distinct("transmission")
    years = await db.cars.distinct("year")
    return {
        "brands": sorted(brands),
        "fuel_types": sorted(fuels),
        "transmissions": sorted(transmissions),
        "years": sorted(years, reverse=True),
    }


@api_router.get("/cars/{car_id}")
async def get_car(car_id: str):
    doc = await db.cars.find_one({"id": car_id}, {"_id": 0})
    if not doc:
        raise HTTPException(status_code=404, detail="Car not found")
    return doc


# ---------- Cars (admin) ----------
@api_router.post("/admin/cars", response_model=Car)
async def admin_create_car(body: CarCreate, _: str = Depends(require_admin)):
    doc = body.model_dump()
    doc.update(
        {
            "id": str(uuid.uuid4()),
            "created_at": now_iso(),
            "updated_at": now_iso(),
        }
    )
    await db.cars.insert_one(doc)
    doc.pop("_id", None)
    return doc


@api_router.put("/admin/cars/{car_id}", response_model=Car)
async def admin_update_car(car_id: str, body: CarUpdate, _: str = Depends(require_admin)):
    update_doc = {k: v for k, v in body.model_dump(exclude_unset=True).items() if v is not None}
    if not update_doc:
        raise HTTPException(status_code=400, detail="No fields to update")
    update_doc["updated_at"] = now_iso()
    result = await db.cars.find_one_and_update(
        {"id": car_id}, {"$set": update_doc}, return_document=True
    )
    if not result:
        raise HTTPException(status_code=404, detail="Car not found")
    result.pop("_id", None)
    return result


@api_router.delete("/admin/cars/{car_id}")
async def admin_delete_car(car_id: str, _: str = Depends(require_admin)):
    res = await db.cars.delete_one({"id": car_id})
    if res.deleted_count == 0:
        raise HTTPException(status_code=404, detail="Car not found")
    return {"deleted": True}


@api_router.get("/admin/stats")
async def admin_stats(_: str = Depends(require_admin)):
    total = await db.cars.count_documents({})
    available = await db.cars.count_documents({"status": "available"})
    sold = await db.cars.count_documents({"status": "sold"})
    sell_submissions = await db.sell_submissions.count_documents({})
    contacts = await db.contact_submissions.count_documents({})
    return {
        "total_cars": total,
        "available_cars": available,
        "sold_cars": sold,
        "sell_submissions": sell_submissions,
        "contact_submissions": contacts,
    }


# ---------- Uploads ----------
@api_router.post("/upload")
async def upload_image(file: UploadFile = File(...), _: str = Depends(require_admin)):
    if not file.content_type or not file.content_type.startswith("image/"):
        raise HTTPException(status_code=400, detail="Only image uploads are allowed")
    ext = file.filename.rsplit(".", 1)[-1].lower() if "." in file.filename else "bin"
    path = f"{APP_NAME}/uploads/{uuid.uuid4()}.{ext}"
    data = await file.read()
    if len(data) > 10 * 1024 * 1024:
        raise HTTPException(status_code=400, detail="File too large (max 10MB)")
    content_type = file.content_type or get_content_type(file.filename)
    result = put_object(path, data, content_type)
    record = {
        "id": str(uuid.uuid4()),
        "storage_path": result["path"],
        "original_filename": file.filename,
        "content_type": content_type,
        "size": result.get("size", len(data)),
        "is_deleted": False,
        "created_at": now_iso(),
    }
    await db.files.insert_one(record)
    return {
        "id": record["id"],
        "url": f"/api/files/{result['path']}",
        "path": result["path"],
    }


@api_router.get("/files/{path:path}")
async def serve_file(path: str):
    record = await db.files.find_one({"storage_path": path, "is_deleted": False})
    if not record:
        raise HTTPException(status_code=404, detail="File not found")
    data, content_type = get_object(path)
    return Response(content=data, media_type=record.get("content_type", content_type))


# ---------- Sell Your Car ----------
@api_router.post("/sell-submissions")
async def submit_sell(body: SellSubmission):
    doc = body.model_dump()
    doc.update({"id": str(uuid.uuid4()), "created_at": now_iso(), "status": "new"})
    await db.sell_submissions.insert_one(doc)
    doc.pop("_id", None)
    return {"success": True, "id": doc["id"]}


@api_router.get("/admin/sell-submissions")
async def list_sell_submissions(_: str = Depends(require_admin)):
    cursor = db.sell_submissions.find({}, {"_id": 0}).sort("created_at", -1)
    items = [d async for d in cursor]
    return {"items": items}


# ---------- Contact ----------
@api_router.post("/contact")
async def submit_contact(body: ContactSubmission):
    doc = body.model_dump()
    doc.update({"id": str(uuid.uuid4()), "created_at": now_iso()})
    await db.contact_submissions.insert_one(doc)
    doc.pop("_id", None)
    return {"success": True}


@api_router.get("/admin/contacts")
async def list_contacts(_: str = Depends(require_admin)):
    cursor = db.contact_submissions.find({}, {"_id": 0}).sort("created_at", -1)
    return {"items": [d async for d in cursor]}


# ---------- Newsletter ----------
@api_router.post("/newsletter")
async def newsletter(body: NewsletterSub):
    exists = await db.newsletter.find_one({"email": body.email})
    if exists:
        return {"success": True, "already": True}
    await db.newsletter.insert_one(
        {"id": str(uuid.uuid4()), "email": body.email, "created_at": now_iso()}
    )
    return {"success": True}


# ---------- Gallery ----------
@api_router.get("/gallery")
async def list_gallery(category: Optional[str] = None):
    q = {}
    if category:
        q["category"] = category
    cursor = db.gallery.find(q, {"_id": 0}).sort("created_at", -1)
    return {"items": [d async for d in cursor]}


@api_router.post("/admin/gallery")
async def add_gallery(body: GalleryItemCreate, _: str = Depends(require_admin)):
    doc = body.model_dump()
    doc.update({"id": str(uuid.uuid4()), "created_at": now_iso()})
    await db.gallery.insert_one(doc)
    doc.pop("_id", None)
    return doc


@api_router.delete("/admin/gallery/{item_id}")
async def delete_gallery(item_id: str, _: str = Depends(require_admin)):
    res = await db.gallery.delete_one({"id": item_id})
    if res.deleted_count == 0:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"deleted": True}


# ---------- Owner ----------
@api_router.get("/owner")
async def get_owner():
    doc = await db.owner.find_one({"_id": "owner_profile"})
    if not doc:
        raise HTTPException(status_code=404, detail="Owner not found")
    doc.pop("_id", None)
    return doc


@api_router.put("/admin/owner")
async def update_owner(body: Owner, _: str = Depends(require_admin)):
    update = body.model_dump()
    update["updated_at"] = now_iso()
    await db.owner.update_one({"_id": "owner_profile"}, {"$set": update}, upsert=True)
    return {"success": True, **update}


# ---------- Register router ----------
app.include_router(api_router)

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