from datetime import datetime
from typing import Any, Dict, Optional

from bson import ObjectId
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from pydantic import BaseModel

from app.db.db import get_col
from app.models.banner import Banner
from app.utility.security import verify_jwt
from app.utility.cloudinary_utils import (
    delete_image_from_cloudinary,
    extract_public_id_from_url,
    upload_image_to_cloudinary,
)


router = APIRouter(prefix="/banners", tags=["banners"])
auth_scheme = HTTPBearer(auto_error=False)


class BannerCreate(BaseModel):
    title: str
    smallText: Optional[str] = None
    isActive: bool = True


class BannerUpdate(BaseModel):
    title: Optional[str] = None
    image: Optional[str] = None
    smallText: Optional[str] = None
    isActive: Optional[bool] = None


def _ensure_object_id(id_str: str) -> ObjectId:
    try:
        return ObjectId(id_str)
    except Exception as exc:  # pragma: no cover - defensive
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid identifier") from exc


def _serialize_banner(doc: Dict[str, Any]) -> Dict[str, Any]:
    return {
        "id": str(doc["_id"]),
        "title": doc["title"],
        "image": doc["image"],
        "smallText": doc.get("smallText"),
        "isActive": doc.get("isActive", True),
        "createdAt": doc.get("createdAt"),
        "updatedAt": doc.get("updatedAt"),
    }


def _require_admin(credentials: HTTPAuthorizationCredentials = Depends(auth_scheme)) -> Dict[str, Any]:
    if not credentials:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated")
    ok, payload = verify_jwt(credentials.credentials)
    if not ok or not payload:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
    if payload.get("role") != "admin":
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Admin access required")
    admins = get_col("admins")
    admin = admins.find_one({"_id": _ensure_object_id(payload["sub"])})
    if not admin:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Admin not found")
    return admin


@router.post("/create", status_code=status.HTTP_201_CREATED)
async def create_banner(
    title: str = Form(...),
    image: UploadFile = File(...),
    smallText: Optional[str] = Form(None),
    isActive: bool = Form(True),
    _admin=Depends(_require_admin),
):
    banners = get_col("banners")
    
    # Upload image to Cloudinary
    image_url = upload_image_to_cloudinary(image, folder="banners")
    
    now = datetime.utcnow()
    payload = {
        "title": title,
        "image": image_url,
        "smallText": smallText,
        "isActive": isActive,
        "createdAt": now,
        "updatedAt": now,
    }
    res = banners.insert_one(payload)
    payload["_id"] = res.inserted_id
    return _serialize_banner(payload)


@router.get("/get/all", status_code=status.HTTP_200_OK)
def list_banners(is_active: Optional[bool] = None):
    banners = get_col("banners")
    query: Dict[str, Any] = {}
    if is_active is not None:
        query["isActive"] = is_active
    return [_serialize_banner(doc) for doc in banners.find(query)]


@router.get("/getbyid/{banner_id}", status_code=status.HTTP_200_OK)
def get_banner(banner_id: str):
    banners = get_col("banners")
    oid = _ensure_object_id(banner_id)
    doc = banners.find_one({"_id": oid})
    if not doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Banner not found")
    return _serialize_banner(doc)


@router.put("/update/{banner_id}", status_code=status.HTTP_200_OK)
async def update_banner(
    banner_id: str,
    title: Optional[str] = Form(None),
    image: Optional[UploadFile] = File(None),
    smallText: Optional[str] = Form(None),
    isActive: Optional[bool] = Form(None),
    _admin=Depends(_require_admin),
):
    banners = get_col("banners")
    oid = _ensure_object_id(banner_id)
    doc = banners.find_one({"_id": oid})
    if not doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Banner not found")
    
    payload: Dict[str, Any] = {}
    
    if title is not None:
        payload["title"] = title
    if smallText is not None:
        payload["smallText"] = smallText
    if isActive is not None:
        payload["isActive"] = isActive
    if image is not None:
        # Delete old image from Cloudinary if exists
        old_image_url = doc.get("image")
        if old_image_url:
            public_id = extract_public_id_from_url(old_image_url)
            if public_id:
                delete_image_from_cloudinary(public_id)
        
        # Upload new image
        payload["image"] = upload_image_to_cloudinary(image, folder="banners")
    
    if not payload:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="No fields to update")
    
    payload["updatedAt"] = datetime.utcnow()
    banners.update_one({"_id": oid}, {"$set": payload})
    updated_doc = banners.find_one({"_id": oid})
    return _serialize_banner(updated_doc)


@router.delete("/delete/{banner_id}", status_code=status.HTTP_200_OK)
def delete_banner(banner_id: str, _admin=Depends(_require_admin)):
    banners = get_col("banners")
    oid = _ensure_object_id(banner_id)
    doc = banners.find_one({"_id": oid})
    if not doc:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Banner not found")
    
    # Delete image from Cloudinary
    image_url = doc.get("image")
    if image_url:
        public_id = extract_public_id_from_url(image_url)
        if public_id:
            delete_image_from_cloudinary(public_id)
    
    banners.delete_one({"_id": oid})
    return {"message": "Banner deleted successfully"}


