from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session, joinedload
from typing import Optional
from datetime import datetime
import secrets

from ...shared.config.database import get_db
from ...shared.config.logging_config import get_logger
from ...security.middleware.auth import get_current_user
from ...auth.models.user import User
from ...shared.utils.role_helpers import is_admin
from ..models.service import Service
from ..models.service_booking import (
    ServiceBooking, 
    ServiceBookingItem, 
    ServicePayment,
    ServiceBookingStatus,
    ServicePaymentStatus,
    ServicePaymentMethod
)
from ...payments.services.stripe_service import StripeService, get_stripe_secret_key, get_stripe_publishable_key
from ...shared.config.settings import settings
from ..schemas.service_booking import (
    CreateServiceBookingRequest,
    CreateServicePaymentIntentRequest,
    ConfirmServicePaymentRequest
)

logger = get_logger(__name__)

router = APIRouter(prefix="/service-bookings", tags=["service-bookings"])

def generate_service_booking_number() -> str:
    prefix = "SB"
    timestamp = datetime.utcnow().strftime("%Y%m%d")
    # Use cryptographically secure random number to prevent enumeration attacks
    random_suffix = secrets.randbelow(9000) + 1000  # Random number between 1000-9999
    return f"{prefix}{timestamp}{random_suffix}"

@router.post("/")
async def create_service_booking(
    booking_data: CreateServiceBookingRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    try:
        services = booking_data.services
        total_amount = booking_data.total_amount
        notes = booking_data.notes
        
        if not services or len(services) == 0:
            raise HTTPException(status_code=400, detail="At least one service is required")
        
        if total_amount <= 0:
            raise HTTPException(status_code=400, detail="Total amount must be greater than 0")
        

        calculated_total = 0
        service_items_data = []
        
        for service_item in services:
            service_id = service_item.service_id
            quantity = service_item.quantity
            
            if not service_id:
                raise HTTPException(status_code=400, detail="Service ID is required for each item")
            

            service = db.query(Service).filter(Service.id == service_id).first()
            if not service:
                raise HTTPException(status_code=404, detail=f"Service with ID {service_id} not found")
            
            if not service.is_active:
                raise HTTPException(status_code=400, detail=f"Service {service.name} is not active")
            
            unit_price = float(service.price)
            item_total = unit_price * quantity
            calculated_total += item_total
            
            service_items_data.append({
                "service": service,
                "quantity": quantity,
                "unit_price": unit_price,
                "total_price": item_total
            })
        

        if abs(calculated_total - total_amount) > 0.01:
            raise HTTPException(
                status_code=400, 
                detail=f"Total amount mismatch. Calculated: {calculated_total}, Provided: {total_amount}"
            )
        

        booking_number = generate_service_booking_number()
        

        service_booking = ServiceBooking(
            booking_number=booking_number,
            user_id=current_user.id,
            total_amount=total_amount,
            status=ServiceBookingStatus.pending,
            notes=notes
        )
        
        db.add(service_booking)
        db.flush()
        

        for item_data in service_items_data:
            booking_item = ServiceBookingItem(
                service_booking_id=service_booking.id,
                service_id=item_data["service"].id,
                quantity=item_data["quantity"],
                unit_price=item_data["unit_price"],
                total_price=item_data["total_price"]
            )
            db.add(booking_item)
        
        db.commit()
        db.refresh(service_booking)
        

        service_booking = db.query(ServiceBooking).options(
            joinedload(ServiceBooking.service_items).joinedload(ServiceBookingItem.service)
        ).filter(ServiceBooking.id == service_booking.id).first()
        

        booking_dict = {
            "id": service_booking.id,
            "booking_number": service_booking.booking_number,
            "user_id": service_booking.user_id,
            "total_amount": float(service_booking.total_amount),
            "status": service_booking.status.value,
            "notes": service_booking.notes,
            "created_at": service_booking.created_at.isoformat() if service_booking.created_at else None,
            "service_items": [
                {
                    "id": item.id,
                    "service_id": item.service_id,
                    "quantity": item.quantity,
                    "unit_price": float(item.unit_price),
                    "total_price": float(item.total_price),
                    "service": {
                        "id": item.service.id,
                        "name": item.service.name,
                        "description": item.service.description,
                        "price": float(item.service.price),
                    }
                }
                for item in service_booking.service_items
            ]
        }
        
        return {
            "status": "success",
            "message": "Service booking created successfully",
            "data": {"service_booking": booking_dict}
        }
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/me")
async def get_my_service_bookings(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    try:
        bookings = db.query(ServiceBooking).options(
            joinedload(ServiceBooking.service_items).joinedload(ServiceBookingItem.service)
        ).filter(ServiceBooking.user_id == current_user.id).order_by(ServiceBooking.created_at.desc()).all()
        
        result = []
        for booking in bookings:
            booking_dict = {
                "id": booking.id,
                "booking_number": booking.booking_number,
                "total_amount": float(booking.total_amount),
                "status": booking.status.value,
                "notes": booking.notes,
                "created_at": booking.created_at.isoformat() if booking.created_at else None,
                "service_items": [
                    {
                        "id": item.id,
                        "service_id": item.service_id,
                        "quantity": item.quantity,
                        "unit_price": float(item.unit_price),
                        "total_price": float(item.total_price),
                        "service": {
                            "id": item.service.id,
                            "name": item.service.name,
                            "description": item.service.description,
                            "price": float(item.service.price),
                        }
                    }
                    for item in booking.service_items
                ]
            }
            result.append(booking_dict)
        
        return {
            "status": "success",
            "data": {"service_bookings": result}
        }
    except Exception as e:
        db.rollback()
        logger.error(f'Error fetching service bookings: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.get("/{id}")
async def get_service_booking_by_id(
    id: int,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    try:
        booking = db.query(ServiceBooking).options(
            joinedload(ServiceBooking.service_items).joinedload(ServiceBookingItem.service)
        ).filter(ServiceBooking.id == id).first()
        
        if not booking:
            raise HTTPException(status_code=404, detail="Service booking not found")
        
        if not is_admin(current_user, db) and booking.user_id != current_user.id:
            raise HTTPException(status_code=403, detail="Forbidden")
        
        booking_dict = {
            "id": booking.id,
            "booking_number": booking.booking_number,
            "user_id": booking.user_id,
            "total_amount": float(booking.total_amount),
            "status": booking.status.value,
            "notes": booking.notes,
            "created_at": booking.created_at.isoformat() if booking.created_at else None,
            "service_items": [
                {
                    "id": item.id,
                    "service_id": item.service_id,
                    "quantity": item.quantity,
                    "unit_price": float(item.unit_price),
                    "total_price": float(item.total_price),
                    "service": {
                        "id": item.service.id,
                        "name": item.service.name,
                        "description": item.service.description,
                        "price": float(item.service.price),
                    }
                }
                for item in booking.service_items
            ]
        }
        
        return {
            "status": "success",
            "data": {"service_booking": booking_dict}
        }
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error fetching service booking by id: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/{id}/payment/stripe/create-intent")
async def create_service_stripe_payment_intent(
    id: int,
    intent_data: CreateServicePaymentIntentRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    try:

        secret_key = get_stripe_secret_key(db)
        if not secret_key:
            secret_key = settings.STRIPE_SECRET_KEY
        
        if not secret_key:
            raise HTTPException(
                status_code=500,
                detail="Stripe is not configured. Please configure Stripe settings in Admin Panel."
            )
        
        amount = intent_data.amount
        currency = intent_data.currency
        
        if amount <= 0:
            raise HTTPException(status_code=400, detail="Amount must be greater than 0")
        

        booking = db.query(ServiceBooking).filter(ServiceBooking.id == id).first()
        if not booking:
            raise HTTPException(status_code=404, detail="Service booking not found")
        
        if not is_admin(current_user, db) and booking.user_id != current_user.id:
            raise HTTPException(status_code=403, detail="Forbidden")
        
        if abs(float(booking.total_amount) - amount) > 0.01:
            raise HTTPException(
                status_code=400,
                detail=f"Amount mismatch. Booking total: {booking.total_amount}, Provided: {amount}"
            )
        

        intent = StripeService.create_payment_intent(
            amount=amount,
            currency=currency,
            description=f"Service Booking #{booking.id}",
            db=db
        )
        

        publishable_key = get_stripe_publishable_key(db)
        if not publishable_key:
            publishable_key = settings.STRIPE_PUBLISHABLE_KEY
        
        if not publishable_key:
            raise HTTPException(
                status_code=500,
                detail="Stripe publishable key is not configured."
            )
        
        return {
            "status": "success",
            "data": {
                "client_secret": intent["client_secret"],
                "payment_intent_id": intent["id"],
                "publishable_key": publishable_key
            }
        }
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error creating service payment intent: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.post("/{id}/payment/stripe/confirm")
async def confirm_service_stripe_payment(
    id: int,
    payment_data: ConfirmServicePaymentRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    try:
        payment_intent_id = payment_data.payment_intent_id
        
        if not payment_intent_id:
            raise HTTPException(status_code=400, detail="payment_intent_id is required")
        

        booking = db.query(ServiceBooking).filter(ServiceBooking.id == id).first()
        if not booking:
            raise HTTPException(status_code=404, detail="Service booking not found")
        
        if not is_admin(current_user, db) and booking.user_id != current_user.id:
            raise HTTPException(status_code=403, detail="Forbidden")
        
        intent_data = StripeService.retrieve_payment_intent(payment_intent_id, db)
        
        if intent_data["status"] != "succeeded":
            raise HTTPException(
                status_code=400,
                detail=f"Payment intent status is {intent_data['status']}, expected 'succeeded'"
            )
        

        amount_paid = intent_data["amount"] / 100
        if abs(float(booking.total_amount) - amount_paid) > 0.01:
            raise HTTPException(
                status_code=400,
                detail="Payment amount does not match booking total"
            )
        

        payment = ServicePayment(
            service_booking_id=booking.id,
            amount=booking.total_amount,
            payment_method=ServicePaymentMethod.stripe,
            payment_status=ServicePaymentStatus.completed,
            transaction_id=payment_intent_id,
            payment_date=datetime.utcnow(),
            notes=f"Stripe payment - Intent: {payment_intent_id}"
        )
        
        db.add(payment)
        

        booking.status = ServiceBookingStatus.confirmed
        
        db.commit()
        db.refresh(payment)
        db.refresh(booking)
        
        return {
            "status": "success",
            "message": "Payment confirmed successfully",
            "data": {
                "payment": {
                    "id": payment.id,
                    "amount": float(payment.amount),
                    "payment_method": payment.payment_method.value,
                    "payment_status": payment.payment_status.value,
                    "transaction_id": payment.transaction_id,
                },
                "service_booking": {
                    "id": booking.id,
                    "booking_number": booking.booking_number,
                    "status": booking.status.value,
                }
            }
        }
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

