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 ..config.database import get_db
from ..middleware.auth import get_current_user, authorize_roles
from ..models.user import User
from ..models.promotion import Promotion, DiscountType
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:
        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:
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/validate')
async def validate_promotion(validation_data: dict, db: Session=Depends(get_db)):
    try:
        code = validation_data.get('code')
        booking_amount = float(validation_data.get('booking_value') or validation_data.get('booking_amount', 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:
        raise HTTPException(status_code=500, detail=str(e))

@router.post('/', dependencies=[Depends(authorize_roles('admin'))])
async def create_promotion(promotion_data: dict, current_user: User=Depends(authorize_roles('admin')), db: Session=Depends(get_db)):
    try:
        code = promotion_data.get('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.get('discount_type')
        discount_value = float(promotion_data.get('discount_value', 0))
        if discount_type == 'percentage' and discount_value > 100:
            raise HTTPException(status_code=400, detail='Percentage discount cannot exceed 100%')
        promotion = Promotion(code=code, name=promotion_data.get('name'), description=promotion_data.get('description'), discount_type=DiscountType(discount_type), discount_value=discount_value, min_booking_amount=float(promotion_data['min_booking_amount']) if promotion_data.get('min_booking_amount') else None, max_discount_amount=float(promotion_data['max_discount_amount']) if promotion_data.get('max_discount_amount') else None, start_date=datetime.fromisoformat(promotion_data['start_date'].replace('Z', '+00:00')) if promotion_data.get('start_date') else None, end_date=datetime.fromisoformat(promotion_data['end_date'].replace('Z', '+00:00')) if promotion_data.get('end_date') else None, usage_limit=promotion_data.get('usage_limit'), used_count=0, is_active=promotion_data.get('status') == 'active' if promotion_data.get('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()
        raise HTTPException(status_code=500, detail=str(e))

@router.put('/{id}', dependencies=[Depends(authorize_roles('admin'))])
async def update_promotion(id: int, promotion_data: dict, 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.get('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.get('discount_type', promotion.discount_type.value if isinstance(promotion.discount_type, DiscountType) else promotion.discount_type)
        discount_value = promotion_data.get('discount_value')
        if discount_value is not None:
            discount_value = float(discount_value)
            if discount_type == 'percentage' and discount_value > 100:
                raise HTTPException(status_code=400, detail='Percentage discount cannot exceed 100%')
        if 'code' in promotion_data:
            promotion.code = promotion_data['code']
        if 'name' in promotion_data:
            promotion.name = promotion_data['name']
        if 'description' in promotion_data:
            promotion.description = promotion_data['description']
        if 'discount_type' in promotion_data:
            promotion.discount_type = DiscountType(promotion_data['discount_type'])
        if 'discount_value' in promotion_data:
            promotion.discount_value = discount_value
        if 'min_booking_amount' in promotion_data:
            promotion.min_booking_amount = float(promotion_data['min_booking_amount']) if promotion_data['min_booking_amount'] else None
        if 'max_discount_amount' in promotion_data:
            promotion.max_discount_amount = float(promotion_data['max_discount_amount']) if promotion_data['max_discount_amount'] else None
        if 'start_date' in promotion_data:
            promotion.start_date = datetime.fromisoformat(promotion_data['start_date'].replace('Z', '+00:00')) if promotion_data['start_date'] else None
        if 'end_date' in promotion_data:
            promotion.end_date = datetime.fromisoformat(promotion_data['end_date'].replace('Z', '+00:00')) if promotion_data['end_date'] else None
        if 'usage_limit' in promotion_data:
            promotion.usage_limit = promotion_data['usage_limit']
        if 'status' in promotion_data:
            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()
        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()
        raise HTTPException(status_code=500, detail=str(e))