from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from sqlalchemy import or_, and_
from typing import Optional, List
from datetime import datetime, date
from decimal import Decimal
from ..config.database import get_db
from ..middleware.auth import get_current_user, authorize_roles
from ..models.user import User
from ..models.rate_plan import RatePlan, RatePlanRule, RatePlanType, RatePlanStatus
from ..models.room_type import RoomType
from ..models.booking import Booking
from pydantic import BaseModel
from typing import Optional as Opt

router = APIRouter(prefix='/rate-plans', tags=['rate-plans'])

# Pydantic models for request/response
class RatePlanRuleCreate(BaseModel):
    rule_type: str
    rule_key: str
    rule_value: Optional[dict] = None
    price_modifier: Optional[float] = None
    discount_percentage: Optional[float] = None
    fixed_adjustment: Optional[float] = None
    priority: int = 100

class RatePlanCreate(BaseModel):
    name: str
    code: str
    description: Optional[str] = None
    plan_type: str
    status: str = 'active'
    base_price_modifier: float = 1.0
    discount_percentage: Optional[float] = None
    fixed_discount: Optional[float] = None
    room_type_id: Optional[int] = None
    min_nights: Optional[int] = None
    max_nights: Optional[int] = None
    advance_days_required: Optional[int] = None
    valid_from: Optional[str] = None
    valid_to: Optional[str] = None
    is_refundable: bool = True
    requires_deposit: bool = False
    deposit_percentage: Optional[float] = None
    cancellation_hours: Optional[int] = None
    corporate_code: Optional[str] = None
    requires_verification: bool = False
    verification_type: Optional[str] = None
    long_stay_nights: Optional[int] = None
    is_package: bool = False
    package_id: Optional[int] = None
    priority: int = 100
    extra_data: Optional[dict] = None
    rules: Optional[List[RatePlanRuleCreate]] = []

class RatePlanUpdate(BaseModel):
    name: Optional[str] = None
    description: Optional[str] = None
    status: Optional[str] = None
    base_price_modifier: Optional[float] = None
    discount_percentage: Optional[float] = None
    fixed_discount: Optional[float] = None
    room_type_id: Optional[int] = None
    min_nights: Optional[int] = None
    max_nights: Optional[int] = None
    advance_days_required: Optional[int] = None
    valid_from: Optional[str] = None
    valid_to: Optional[str] = None
    is_refundable: Optional[bool] = None
    requires_deposit: Optional[bool] = None
    deposit_percentage: Optional[float] = None
    cancellation_hours: Optional[int] = None
    corporate_code: Optional[str] = None
    requires_verification: Optional[bool] = None
    verification_type: Optional[str] = None
    long_stay_nights: Optional[int] = None
    package_id: Optional[int] = None
    priority: Optional[int] = None
    extra_data: Optional[dict] = None

@router.get('/')
async def get_rate_plans(
    search: Optional[str] = Query(None),
    status_filter: Optional[str] = Query(None, alias='status'),
    plan_type: Optional[str] = Query(None),
    room_type_id: Optional[int] = 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(RatePlan)
        
        if search:
            query = query.filter(
                or_(
                    RatePlan.name.like(f'%{search}%'),
                    RatePlan.code.like(f'%{search}%'),
                    RatePlan.description.like(f'%{search}%')
                )
            )
        
        if status_filter:
            try:
                query = query.filter(RatePlan.status == RatePlanStatus(status_filter))
            except ValueError:
                pass
        
        if plan_type:
            try:
                query = query.filter(RatePlan.plan_type == RatePlanType(plan_type))
            except ValueError:
                pass
        
        if room_type_id:
            query = query.filter(
                or_(
                    RatePlan.room_type_id == room_type_id,
                    RatePlan.room_type_id.is_(None)
                )
            )
        
        total = query.count()
        offset = (page - 1) * limit
        rate_plans = query.order_by(RatePlan.priority.asc(), RatePlan.created_at.desc()).offset(offset).limit(limit).all()
        
        result = []
        for plan in rate_plans:
            plan_dict = {
                'id': plan.id,
                'name': plan.name,
                'code': plan.code,
                'description': plan.description,
                'plan_type': plan.plan_type.value if isinstance(plan.plan_type, RatePlanType) else plan.plan_type,
                'status': plan.status.value if isinstance(plan.status, RatePlanStatus) else plan.status,
                'base_price_modifier': float(plan.base_price_modifier) if plan.base_price_modifier else 1.0,
                'discount_percentage': float(plan.discount_percentage) if plan.discount_percentage else None,
                'fixed_discount': float(plan.fixed_discount) if plan.fixed_discount else None,
                'room_type_id': plan.room_type_id,
                'room_type_name': plan.room_type.name if plan.room_type else None,
                'min_nights': plan.min_nights,
                'max_nights': plan.max_nights,
                'advance_days_required': plan.advance_days_required,
                'valid_from': plan.valid_from.isoformat() if plan.valid_from else None,
                'valid_to': plan.valid_to.isoformat() if plan.valid_to else None,
                'is_refundable': plan.is_refundable,
                'requires_deposit': plan.requires_deposit,
                'deposit_percentage': float(plan.deposit_percentage) if plan.deposit_percentage else None,
                'cancellation_hours': plan.cancellation_hours,
                'corporate_code': plan.corporate_code,
                'requires_verification': plan.requires_verification,
                'verification_type': plan.verification_type,
                'long_stay_nights': plan.long_stay_nights,
                'is_package': plan.is_package,
                'package_id': plan.package_id,
                'priority': plan.priority,
                'extra_data': plan.extra_data,
                'created_at': plan.created_at.isoformat() if plan.created_at else None,
                'updated_at': plan.updated_at.isoformat() if plan.updated_at else None,
            }
            result.append(plan_dict)
        
        return {
            'status': 'success',
            'data': {
                'rate_plans': 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('/{id}')
async def get_rate_plan(id: int, db: Session = Depends(get_db)):
    try:
        plan = db.query(RatePlan).filter(RatePlan.id == id).first()
        if not plan:
            raise HTTPException(status_code=404, detail='Rate plan not found')
        
        rules = db.query(RatePlanRule).filter(RatePlanRule.rate_plan_id == id).order_by(RatePlanRule.priority.asc()).all()
        
        plan_dict = {
            'id': plan.id,
            'name': plan.name,
            'code': plan.code,
            'description': plan.description,
            'plan_type': plan.plan_type.value if isinstance(plan.plan_type, RatePlanType) else plan.plan_type,
            'status': plan.status.value if isinstance(plan.status, RatePlanStatus) else plan.status,
            'base_price_modifier': float(plan.base_price_modifier) if plan.base_price_modifier else 1.0,
            'discount_percentage': float(plan.discount_percentage) if plan.discount_percentage else None,
            'fixed_discount': float(plan.fixed_discount) if plan.fixed_discount else None,
            'room_type_id': plan.room_type_id,
            'room_type_name': plan.room_type.name if plan.room_type else None,
            'min_nights': plan.min_nights,
            'max_nights': plan.max_nights,
            'advance_days_required': plan.advance_days_required,
            'valid_from': plan.valid_from.isoformat() if plan.valid_from else None,
            'valid_to': plan.valid_to.isoformat() if plan.valid_to else None,
            'is_refundable': plan.is_refundable,
            'requires_deposit': plan.requires_deposit,
            'deposit_percentage': float(plan.deposit_percentage) if plan.deposit_percentage else None,
            'cancellation_hours': plan.cancellation_hours,
            'corporate_code': plan.corporate_code,
            'requires_verification': plan.requires_verification,
            'verification_type': plan.verification_type,
            'long_stay_nights': plan.long_stay_nights,
            'is_package': plan.is_package,
            'package_id': plan.package_id,
            'priority': plan.priority,
            'extra_data': plan.extra_data,
            'rules': [
                {
                    'id': rule.id,
                    'rule_type': rule.rule_type,
                    'rule_key': rule.rule_key,
                    'rule_value': rule.rule_value,
                    'price_modifier': float(rule.price_modifier) if rule.price_modifier else None,
                    'discount_percentage': float(rule.discount_percentage) if rule.discount_percentage else None,
                    'fixed_adjustment': float(rule.fixed_adjustment) if rule.fixed_adjustment else None,
                    'priority': rule.priority,
                }
                for rule in rules
            ],
            'created_at': plan.created_at.isoformat() if plan.created_at else None,
            'updated_at': plan.updated_at.isoformat() if plan.updated_at else None,
        }
        
        return {'status': 'success', 'data': {'rate_plan': plan_dict}}
    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_rate_plan(plan_data: RatePlanCreate, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    try:
        # Check if code already exists
        existing = db.query(RatePlan).filter(RatePlan.code == plan_data.code).first()
        if existing:
            raise HTTPException(status_code=400, detail='Rate plan code already exists')
        
        # Validate room_type_id if provided
        if plan_data.room_type_id:
            room_type = db.query(RoomType).filter(RoomType.id == plan_data.room_type_id).first()
            if not room_type:
                raise HTTPException(status_code=404, detail='Room type not found')
        
        # Create rate plan
        rate_plan = RatePlan(
            name=plan_data.name,
            code=plan_data.code,
            description=plan_data.description,
            plan_type=RatePlanType(plan_data.plan_type),
            status=RatePlanStatus(plan_data.status),
            base_price_modifier=Decimal(str(plan_data.base_price_modifier)),
            discount_percentage=Decimal(str(plan_data.discount_percentage)) if plan_data.discount_percentage else None,
            fixed_discount=Decimal(str(plan_data.fixed_discount)) if plan_data.fixed_discount else None,
            room_type_id=plan_data.room_type_id,
            min_nights=plan_data.min_nights,
            max_nights=plan_data.max_nights,
            advance_days_required=plan_data.advance_days_required,
            valid_from=datetime.strptime(plan_data.valid_from, '%Y-%m-%d').date() if plan_data.valid_from else None,
            valid_to=datetime.strptime(plan_data.valid_to, '%Y-%m-%d').date() if plan_data.valid_to else None,
            is_refundable=plan_data.is_refundable,
            requires_deposit=plan_data.requires_deposit,
            deposit_percentage=Decimal(str(plan_data.deposit_percentage)) if plan_data.deposit_percentage else None,
            cancellation_hours=plan_data.cancellation_hours,
            corporate_code=plan_data.corporate_code,
            requires_verification=plan_data.requires_verification,
            verification_type=plan_data.verification_type,
            long_stay_nights=plan_data.long_stay_nights,
            is_package=plan_data.is_package,
            package_id=plan_data.package_id,
            priority=plan_data.priority,
            extra_data=plan_data.extra_data,
        )
        
        db.add(rate_plan)
        db.flush()
        
        # Create rules
        if plan_data.rules:
            for rule_data in plan_data.rules:
                rule = RatePlanRule(
                    rate_plan_id=rate_plan.id,
                    rule_type=rule_data.rule_type,
                    rule_key=rule_data.rule_key,
                    rule_value=rule_data.rule_value,
                    price_modifier=Decimal(str(rule_data.price_modifier)) if rule_data.price_modifier else None,
                    discount_percentage=Decimal(str(rule_data.discount_percentage)) if rule_data.discount_percentage else None,
                    fixed_adjustment=Decimal(str(rule_data.fixed_adjustment)) if rule_data.fixed_adjustment else None,
                    priority=rule_data.priority,
                )
                db.add(rule)
        
        db.commit()
        db.refresh(rate_plan)
        
        return {'status': 'success', 'message': 'Rate plan created successfully', 'data': {'rate_plan_id': rate_plan.id}}
    except HTTPException:
        raise
    except ValueError as e:
        raise HTTPException(status_code=400, detail=f'Invalid enum value: {str(e)}')
    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_rate_plan(id: int, plan_data: RatePlanUpdate, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    try:
        rate_plan = db.query(RatePlan).filter(RatePlan.id == id).first()
        if not rate_plan:
            raise HTTPException(status_code=404, detail='Rate plan not found')
        
        # Update fields
        if plan_data.name is not None:
            rate_plan.name = plan_data.name
        if plan_data.description is not None:
            rate_plan.description = plan_data.description
        if plan_data.status is not None:
            rate_plan.status = RatePlanStatus(plan_data.status)
        if plan_data.base_price_modifier is not None:
            rate_plan.base_price_modifier = Decimal(str(plan_data.base_price_modifier))
        if plan_data.discount_percentage is not None:
            rate_plan.discount_percentage = Decimal(str(plan_data.discount_percentage))
        if plan_data.fixed_discount is not None:
            rate_plan.fixed_discount = Decimal(str(plan_data.fixed_discount))
        if plan_data.room_type_id is not None:
            if plan_data.room_type_id:
                room_type = db.query(RoomType).filter(RoomType.id == plan_data.room_type_id).first()
                if not room_type:
                    raise HTTPException(status_code=404, detail='Room type not found')
            rate_plan.room_type_id = plan_data.room_type_id
        if plan_data.min_nights is not None:
            rate_plan.min_nights = plan_data.min_nights
        if plan_data.max_nights is not None:
            rate_plan.max_nights = plan_data.max_nights
        if plan_data.advance_days_required is not None:
            rate_plan.advance_days_required = plan_data.advance_days_required
        if plan_data.valid_from is not None:
            rate_plan.valid_from = datetime.strptime(plan_data.valid_from, '%Y-%m-%d').date() if plan_data.valid_from else None
        if plan_data.valid_to is not None:
            rate_plan.valid_to = datetime.strptime(plan_data.valid_to, '%Y-%m-%d').date() if plan_data.valid_to else None
        if plan_data.is_refundable is not None:
            rate_plan.is_refundable = plan_data.is_refundable
        if plan_data.requires_deposit is not None:
            rate_plan.requires_deposit = plan_data.requires_deposit
        if plan_data.deposit_percentage is not None:
            rate_plan.deposit_percentage = Decimal(str(plan_data.deposit_percentage)) if plan_data.deposit_percentage else None
        if plan_data.cancellation_hours is not None:
            rate_plan.cancellation_hours = plan_data.cancellation_hours
        if plan_data.corporate_code is not None:
            rate_plan.corporate_code = plan_data.corporate_code
        if plan_data.requires_verification is not None:
            rate_plan.requires_verification = plan_data.requires_verification
        if plan_data.verification_type is not None:
            rate_plan.verification_type = plan_data.verification_type
        if plan_data.long_stay_nights is not None:
            rate_plan.long_stay_nights = plan_data.long_stay_nights
        if plan_data.package_id is not None:
            rate_plan.package_id = plan_data.package_id
        if plan_data.priority is not None:
            rate_plan.priority = plan_data.priority
        if plan_data.extra_data is not None:
            rate_plan.extra_data = plan_data.extra_data
        
        db.commit()
        
        return {'status': 'success', 'message': 'Rate plan updated successfully'}
    except HTTPException:
        raise
    except ValueError as e:
        raise HTTPException(status_code=400, detail=f'Invalid enum value: {str(e)}')
    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_rate_plan(id: int, current_user: User = Depends(get_current_user), db: Session = Depends(get_db)):
    try:
        rate_plan = db.query(RatePlan).filter(RatePlan.id == id).first()
        if not rate_plan:
            raise HTTPException(status_code=404, detail='Rate plan not found')
        
        # Check if rate plan is used in bookings
        booking_count = db.query(Booking).filter(Booking.rate_plan_id == id).count()
        if booking_count > 0:
            raise HTTPException(status_code=400, detail=f'Cannot delete rate plan. It is used in {booking_count} booking(s)')
        
        # Delete rules first
        db.query(RatePlanRule).filter(RatePlanRule.rate_plan_id == id).delete()
        
        db.delete(rate_plan)
        db.commit()
        
        return {'status': 'success', 'message': 'Rate plan deleted successfully'}
    except HTTPException:
        raise
    except Exception as e:
        db.rollback()
        raise HTTPException(status_code=500, detail=str(e))

@router.get('/available/{room_type_id}')
async def get_available_rate_plans(
    room_type_id: int,
    check_in: str = Query(...),
    check_out: str = Query(...),
    num_nights: Optional[int] = Query(None),
    db: Session = Depends(get_db)
):
    """Get available rate plans for a room type and date range"""
    try:
        check_in_date = datetime.strptime(check_in, '%Y-%m-%d').date()
        check_out_date = datetime.strptime(check_out, '%Y-%m-%d').date()
        
        if num_nights is None:
            num_nights = (check_out_date - check_in_date).days
        
        today = date.today()
        advance_days = (check_in_date - today).days
        
        # Query rate plans
        query = db.query(RatePlan).filter(
            RatePlan.status == RatePlanStatus.active,
            or_(
                RatePlan.room_type_id == room_type_id,
                RatePlan.room_type_id.is_(None)
            )
        )
        
        # Filter by date range
        query = query.filter(
            or_(
                RatePlan.valid_from.is_(None),
                RatePlan.valid_from <= check_in_date
            ),
            or_(
                RatePlan.valid_to.is_(None),
                RatePlan.valid_to >= check_out_date
            )
        )
        
        # Filter by advance days
        query = query.filter(
            or_(
                RatePlan.advance_days_required.is_(None),
                RatePlan.advance_days_required <= advance_days
            )
        )
        
        # Filter by nights
        query = query.filter(
            or_(
                RatePlan.min_nights.is_(None),
                RatePlan.min_nights <= num_nights
            ),
            or_(
                RatePlan.max_nights.is_(None),
                RatePlan.max_nights >= num_nights
            )
        )
        
        rate_plans = query.order_by(RatePlan.priority.asc()).all()
        
        result = []
        for plan in rate_plans:
            plan_dict = {
                'id': plan.id,
                'name': plan.name,
                'code': plan.code,
                'description': plan.description,
                'plan_type': plan.plan_type.value if isinstance(plan.plan_type, RatePlanType) else plan.plan_type,
                'base_price_modifier': float(plan.base_price_modifier) if plan.base_price_modifier else 1.0,
                'discount_percentage': float(plan.discount_percentage) if plan.discount_percentage else None,
                'fixed_discount': float(plan.fixed_discount) if plan.fixed_discount else None,
                'is_refundable': plan.is_refundable,
                'requires_deposit': plan.requires_deposit,
                'deposit_percentage': float(plan.deposit_percentage) if plan.deposit_percentage else None,
                'cancellation_hours': plan.cancellation_hours,
                'requires_verification': plan.requires_verification,
                'verification_type': plan.verification_type,
            }
            result.append(plan_dict)
        
        return {'status': 'success', 'data': {'rate_plans': result}}
    except ValueError as e:
        raise HTTPException(status_code=400, detail=f'Invalid date format: {str(e)}')
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

