from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from sqlalchemy import or_
from typing import Optional
from datetime import datetime
from ...shared.config.database import get_db
from ...shared.config.logging_config import get_logger
from ...security.middleware.auth import get_current_user, authorize_roles
from ...auth.models.user import User
from ..models.promotion import Promotion, DiscountType
from ..schemas.promotion import (
    ValidatePromotionRequest,
    CreatePromotionRequest,
    UpdatePromotionRequest
)

logger = get_logger(__name__)
router = APIRouter(prefix='/promotions', tags=['promotions'])

@router.get('/')
async def get_promotions(search: Optional[str]=Query(None), status_filter: Optional[str]=Query(None, alias='status'), type: Optional[str]=Query(None), page: int=Query(1, ge=1), limit: int=Query(10, ge=1, le=100), db: Session=Depends(get_db)):
    try:
        query = db.query(Promotion)
        if search:
            query = query.filter(or_(Promotion.code.like(f'%{search}%'), Promotion.name.like(f'%{search}%')))
        if status_filter:
            is_active = status_filter == 'active'
            query = query.filter(Promotion.is_active == is_active)
        if type:
            try:
                query = query.filter(Promotion.discount_type == DiscountType(type))
            except ValueError:
                pass
        total = query.count()
        offset = (page - 1) * limit
        promotions = query.order_by(Promotion.created_at.desc()).offset(offset).limit(limit).all()
        result = []
        for promo in promotions:
            promo_dict = {'id': promo.id, 'code': promo.code, 'name': promo.name, 'description': promo.description, 'discount_type': promo.discount_type.value if isinstance(promo.discount_type, DiscountType) else promo.discount_type, 'discount_value': float(promo.discount_value) if promo.discount_value else 0.0, 'min_booking_amount': float(promo.min_booking_amount) if promo.min_booking_amount else None, 'max_discount_amount': float(promo.max_discount_amount) if promo.max_discount_amount else None, 'start_date': promo.start_date.isoformat() if promo.start_date else None, 'end_date': promo.end_date.isoformat() if promo.end_date else None, 'usage_limit': promo.usage_limit, 'used_count': promo.used_count, 'is_active': promo.is_active, 'created_at': promo.created_at.isoformat() if promo.created_at else None}
            result.append(promo_dict)
        return {'status': 'success', 'data': {'promotions': result, 'pagination': {'total': total, 'page': page, 'limit': limit, 'totalPages': (total + limit - 1) // limit}}}
    except Exception as e:
        db.rollback()
        logger.error(f'Error fetching promotions: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/{code}')
async def get_promotion_by_code(code: str, db: Session=Depends(get_db)):
    try:
        promotion = db.query(Promotion).filter(Promotion.code == code).first()
        if not promotion:
            raise HTTPException(status_code=404, detail='Promotion not found')
        promo_dict = {'id': promotion.id, 'code': promotion.code, 'name': promotion.name, 'description': promotion.description, 'discount_type': promotion.discount_type.value if isinstance(promotion.discount_type, DiscountType) else promotion.discount_type, 'discount_value': float(promotion.discount_value) if promotion.discount_value else 0.0, 'min_booking_amount': float(promotion.min_booking_amount) if promotion.min_booking_amount else None, 'max_discount_amount': float(promotion.max_discount_amount) if promotion.max_discount_amount else None, 'start_date': promotion.start_date.isoformat() if promotion.start_date else None, 'end_date': promotion.end_date.isoformat() if promotion.end_date else None, 'usage_limit': promotion.usage_limit, 'used_count': promotion.used_count, 'is_active': promotion.is_active}
        return {'status': 'success', 'data': {'promotion': promo_dict}}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error fetching promotion by code: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/validate')
async def validate_promotion(validation_data: ValidatePromotionRequest, db: Session=Depends(get_db)):
    try:
        code = validation_data.code
        booking_amount = float(validation_data.booking_value or validation_data.booking_amount or 0)
        promotion = db.query(Promotion).filter(Promotion.code == code).first()
        if not promotion:
            raise HTTPException(status_code=404, detail='Promotion code not found')
        if not promotion.is_active:
            raise HTTPException(status_code=400, detail='Promotion is not active')
        now = datetime.utcnow()
        if promotion.start_date and now < promotion.start_date:
            raise HTTPException(status_code=400, detail='Promotion is not valid at this time')
        if promotion.end_date and now > promotion.end_date:
            raise HTTPException(status_code=400, detail='Promotion is not valid at this time')
        if promotion.usage_limit and promotion.used_count >= promotion.usage_limit:
            raise HTTPException(status_code=400, detail='Promotion usage limit reached')
        if promotion.min_booking_amount and booking_amount < float(promotion.min_booking_amount):
            raise HTTPException(status_code=400, detail=f'Minimum booking amount is {promotion.min_booking_amount}')
        discount_amount = promotion.calculate_discount(booking_amount)
        final_amount = booking_amount - discount_amount
        return {'success': True, 'status': 'success', 'data': {'promotion': {'id': promotion.id, 'code': promotion.code, 'name': promotion.name, 'description': promotion.description, 'discount_type': promotion.discount_type.value if hasattr(promotion.discount_type, 'value') else str(promotion.discount_type), 'discount_value': float(promotion.discount_value) if promotion.discount_value else 0, 'min_booking_amount': float(promotion.min_booking_amount) if promotion.min_booking_amount else None, 'max_discount_amount': float(promotion.max_discount_amount) if promotion.max_discount_amount else None, 'start_date': promotion.start_date.isoformat() if promotion.start_date else None, 'end_date': promotion.end_date.isoformat() if promotion.end_date else None, 'usage_limit': promotion.usage_limit, 'used_count': promotion.used_count, 'status': 'active' if promotion.is_active else 'inactive'}, 'discount': discount_amount, 'original_amount': booking_amount, 'discount_amount': discount_amount, 'final_amount': final_amount}, 'message': 'Promotion validated successfully'}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error validating promotion: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/', dependencies=[Depends(authorize_roles('admin'))])
async def create_promotion(promotion_data: CreatePromotionRequest, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
    try:
        code = promotion_data.code
        existing = db.query(Promotion).filter(Promotion.code == code).first()
        if existing:
            raise HTTPException(status_code=400, detail='Promotion code already exists')
        discount_type = promotion_data.discount_type
        discount_value = promotion_data.discount_value
        promotion = Promotion(
            code=code,
            name=promotion_data.name,
            description=promotion_data.description,
            discount_type=DiscountType(discount_type),
            discount_value=discount_value,
            min_booking_amount=promotion_data.min_booking_amount,
            max_discount_amount=promotion_data.max_discount_amount,
            start_date=datetime.fromisoformat(promotion_data.start_date.replace('Z', '+00:00')) if promotion_data.start_date else None,
            end_date=datetime.fromisoformat(promotion_data.end_date.replace('Z', '+00:00')) if promotion_data.end_date else None,
            usage_limit=promotion_data.usage_limit,
            used_count=0,
            is_active=promotion_data.status == 'active' if promotion_data.status else True
        )
        db.add(promotion)
        db.commit()
        db.refresh(promotion)
        return {'status': 'success', 'message': 'Promotion created successfully', 'data': {'promotion': promotion}}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error creating promotion: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.put('/{id}', dependencies=[Depends(authorize_roles('admin'))])
async def update_promotion(id: int, promotion_data: UpdatePromotionRequest, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
    try:
        promotion = db.query(Promotion).filter(Promotion.id == id).first()
        if not promotion:
            raise HTTPException(status_code=404, detail='Promotion not found')
        code = promotion_data.code
        if code and code != promotion.code:
            existing = db.query(Promotion).filter(Promotion.code == code, Promotion.id != id).first()
            if existing:
                raise HTTPException(status_code=400, detail='Promotion code already exists')
        discount_type = promotion_data.discount_type or (promotion.discount_type.value if isinstance(promotion.discount_type, DiscountType) else promotion.discount_type)
        discount_value = promotion_data.discount_value
        if discount_value is not None and discount_type == 'percentage' and discount_value > 100:
            raise HTTPException(status_code=400, detail='Percentage discount cannot exceed 100%')
        if promotion_data.code is not None:
            promotion.code = promotion_data.code
        if promotion_data.name is not None:
            promotion.name = promotion_data.name
        if promotion_data.description is not None:
            promotion.description = promotion_data.description
        if promotion_data.discount_type is not None:
            promotion.discount_type = DiscountType(promotion_data.discount_type)
        if promotion_data.discount_value is not None:
            promotion.discount_value = promotion_data.discount_value
        if promotion_data.min_booking_amount is not None:
            promotion.min_booking_amount = promotion_data.min_booking_amount
        if promotion_data.max_discount_amount is not None:
            promotion.max_discount_amount = promotion_data.max_discount_amount
        if promotion_data.start_date is not None:
            promotion.start_date = datetime.fromisoformat(promotion_data.start_date.replace('Z', '+00:00')) if promotion_data.start_date else None
        if promotion_data.end_date is not None:
            promotion.end_date = datetime.fromisoformat(promotion_data.end_date.replace('Z', '+00:00')) if promotion_data.end_date else None
        if promotion_data.usage_limit is not None:
            promotion.usage_limit = promotion_data.usage_limit
        if promotion_data.status is not None:
            promotion.is_active = promotion_data.status == 'active'
        db.commit()
        db.refresh(promotion)
        return {'status': 'success', 'message': 'Promotion updated successfully', 'data': {'promotion': promotion}}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error updating promotion: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))

@router.delete('/{id}', dependencies=[Depends(authorize_roles('admin'))])
async def delete_promotion(id: int, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
    try:
        promotion = db.query(Promotion).filter(Promotion.id == id).first()
        if not promotion:
            raise HTTPException(status_code=404, detail='Promotion not found')
        db.delete(promotion)
        db.commit()
        return {'status': 'success', 'message': 'Promotion deleted successfully'}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        logger.error(f'Error deleting promotion: {str(e)}', exc_info=True)
        raise HTTPException(status_code=500, detail=str(e))