"""
Booking Routes for Scheduling
Handles booking creation and management
"""

from flask import request, jsonify, render_template
from flask_login import login_required, current_user
from datetime import datetime, timedelta
from . import scheduling_bp
from app.extensions import db
from app.models.booking import Booking, BookingStatus, RecurringBooking
from app.models.availability import Availability, AvailabilityStatus
from app.models.user import User
from app.services.notification_service import NotificationService
import json


@scheduling_bp.route('/bookings', methods=['GET'])
@login_required
def bookings_list():
    """List all bookings for current user"""
    return render_template('scheduling/bookings.html')


@scheduling_bp.route('/api/bookings', methods=['GET'])
@login_required
def get_bookings():
    """Get bookings for current user"""
    # Determine if user is a worker or client
    as_worker = request.args.get('as_worker', 'false').lower() == 'true'
    status = request.args.get('status')
    start_date = request.args.get('start_date')
    end_date = request.args.get('end_date')
    
    query = Booking.query
    
    if as_worker:
        query = query.filter(Booking.worker_id == current_user.id)
    else:
        query = query.filter(Booking.client_id == current_user.id)
    
    if status:
        query = query.filter(Booking.status == status)
    
    if start_date:
        try:
            start = datetime.strptime(start_date, '%Y-%m-%d').date()
            query = query.filter(Booking.booking_date >= start)
        except ValueError:
            pass
    
    if end_date:
        try:
            end = datetime.strptime(end_date, '%Y-%m-%d').date()
            query = query.filter(Booking.booking_date <= end)
        except ValueError:
            pass
    
    bookings = query.order_by(Booking.booking_date.desc()).all()
    
    return jsonify({
        'success': True,
        'bookings': [booking.to_dict() for booking in bookings]
    })


@scheduling_bp.route('/api/bookings/upcoming', methods=['GET'])
@login_required
def get_upcoming_bookings():
    """Get upcoming bookings for current user as worker"""
    # Get bookings from today onwards, ordered by date
    today = datetime.now().date()
    
    bookings = Booking.query.filter(
        Booking.worker_id == current_user.id,
        Booking.booking_date >= today,
        Booking.status.in_([BookingStatus.PENDING, BookingStatus.CONFIRMED])
    ).order_by(Booking.booking_date.asc()).limit(5).all()
    
    return jsonify({
        'success': True,
        'bookings': [booking.to_dict() for booking in bookings]
    })


@scheduling_bp.route('/api/bookings', methods=['POST'])
@login_required
def create_booking():
    """Create a new booking"""
    data = request.get_json()
    
    required_fields = ['worker_id', 'booking_date', 'start_time', 'duration_minutes', 'service_type']
    for field in required_fields:
        if field not in data:
            return jsonify({'error': f'{field} is required'}), 400
    
    try:
        # Parse date and time
        booking_date = datetime.strptime(data['booking_date'], '%Y-%m-%d').date()
        start_time = datetime.strptime(data['start_time'], '%H:%M').time()
        duration_minutes = int(data['duration_minutes'])
        
        # Calculate end time
        start_datetime = datetime.combine(booking_date, start_time)
        end_datetime = start_datetime + timedelta(minutes=duration_minutes)
        end_time = end_datetime.time()
        
        # Check worker availability
        availability = check_availability(
            data['worker_id'],
            booking_date,
            start_time,
            end_time
        )
        
        if not availability['available']:
            return jsonify({'error': availability['reason']}), 400
        
        # Create booking
        booking = Booking(
            client_id=current_user.id,
            worker_id=data['worker_id'],
            job_id=data.get('job_id'),
            availability_id=availability.get('availability_id'),
            booking_date=booking_date,
            start_time=start_time,
            end_time=end_time,
            duration_minutes=duration_minutes,
            service_type=data['service_type'],
            description=data.get('description'),
            location=data.get('location'),
            estimated_cost=data.get('estimated_cost'),
            client_notes=data.get('notes'),
            status=BookingStatus.PENDING,
            confirmation_code=Booking.generate_confirmation_code()
        )
        
        # Book the availability slot if found
        if availability.get('availability_slot'):
            availability['availability_slot'].book_slot()
        
        db.session.add(booking)
        db.session.commit()
        
        # Sync to Google Calendar if worker has calendar connected
        try:
            from app.services.calendar_sync_service import CalendarSyncService
            if booking.status in [BookingStatus.CONFIRMED, 'scheduled']:
                CalendarSyncService.upsert_booking_event(booking.worker_id, booking)
        except Exception as e:
            import logging
            logging.warning(f"Failed to sync booking to calendar: {str(e)}")
        
        # Send notification to worker
        NotificationService.create_notification(
            user_id=booking.worker_id,
            title="New Booking Request",
            message=f"You have a new booking request from {current_user.first_name} for {booking.service_type} on {booking.booking_date}",
            notification_type='booking_request',
            related_id=booking.id
        )
        
        return jsonify({
            'success': True,
            'booking': booking.to_dict()
        })
        
    except ValueError as e:
        return jsonify({'error': f'Invalid data: {str(e)}'}), 400
    except Exception as e:
        db.session.rollback()
        return jsonify({'error': str(e)}), 500


@scheduling_bp.route('/api/bookings/<int:booking_id>/confirm', methods=['POST'])
@login_required
def confirm_booking(booking_id):
    """Confirm a booking (worker action)"""
    booking = Booking.query.get_or_404(booking_id)
    
    # Check if user is the worker
    if booking.worker_id != current_user.id:
        return jsonify({'error': 'Unauthorized'}), 403
    
    if booking.confirm():
        db.session.commit()
        
        # Sync to Google Calendar after confirmation
        try:
            from app.services.calendar_sync_service import CalendarSyncService
            CalendarSyncService.upsert_booking_event(booking.worker_id, booking)
        except Exception as e:
            import logging
            logging.warning(f"Failed to sync confirmed booking to calendar: {str(e)}")
        
        # Schedule reminders
        try:
            from app.services.reminder_service import ReminderService
            ReminderService.schedule_booking_reminders(booking.id)
        except Exception as e:
            import logging
            logging.warning(f"Failed to schedule reminders: {str(e)}")
        
        # Send notification to client
        NotificationService.create_notification(
            user_id=booking.client_id,
            title="Booking Confirmed",
            message=f"Your booking for {booking.service_type} on {booking.booking_date} has been confirmed",
            notification_type='booking_confirmed',
            related_id=booking.id
        )
        
        return jsonify({
            'success': True,
            'booking': booking.to_dict()
        })
    
    return jsonify({'error': 'Cannot confirm booking in current state'}), 400


@scheduling_bp.route('/api/bookings/<int:booking_id>/cancel', methods=['POST'])
@login_required
def cancel_booking(booking_id):
    """Cancel a booking"""
    booking = Booking.query.get_or_404(booking_id)
    data = request.get_json()
    
    # Check if user is either worker or client
    if booking.worker_id != current_user.id and booking.client_id != current_user.id:
        return jsonify({'error': 'Unauthorized'}), 403
    
    if booking.cancel(data.get('reason')):
        db.session.commit()
        
        # Remove from Google Calendar
        try:
            from app.services.calendar_sync_service import CalendarSyncService
            CalendarSyncService.delete_booking_event(booking.worker_id, booking)
        except Exception as e:
            import logging
            logging.warning(f"Failed to remove booking from calendar: {str(e)}")
        
        # Cancel reminders
        try:
            from app.services.reminder_service import ReminderService
            ReminderService.cancel_booking_reminders(booking.id)
        except Exception as e:
            import logging
            logging.warning(f"Failed to cancel reminders: {str(e)}")
        
        # Send notification to the other party
        if current_user.id == booking.client_id:
            recipient_id = booking.worker_id
            message = f"Booking for {booking.service_type} on {booking.booking_date} has been cancelled by the client"
        else:
            recipient_id = booking.client_id
            message = f"Booking for {booking.service_type} on {booking.booking_date} has been cancelled by the worker"
        
        NotificationService.create_notification(
            user_id=recipient_id,
            title="Booking Cancelled",
            message=message,
            notification_type='booking_cancelled',
            related_id=booking.id
        )
        
        return jsonify({
            'success': True,
            'booking': booking.to_dict()
        })
    
    return jsonify({'error': 'Cannot cancel booking in current state'}), 400


@scheduling_bp.route('/api/recurring-bookings', methods=['GET'])
@login_required
def get_recurring_bookings():
    """Get recurring bookings for current user"""
    as_worker = request.args.get('as_worker', 'false').lower() == 'true'
    
    if as_worker:
        recurring = RecurringBooking.query.filter_by(
            worker_id=current_user.id,
            is_active=True
        ).all()
    else:
        recurring = RecurringBooking.query.filter_by(
            client_id=current_user.id,
            is_active=True
        ).all()
    
    return jsonify({
        'success': True,
        'recurring_bookings': [r.to_dict() for r in recurring]
    })


@scheduling_bp.route('/api/recurring-bookings', methods=['POST'])
@login_required
def create_recurring_booking():
    """Create a recurring booking"""
    data = request.get_json()
    
    required_fields = ['worker_id', 'recurrence_type', 'start_time', 'duration_minutes', 
                      'service_type', 'start_date']
    for field in required_fields:
        if field not in data:
            return jsonify({'error': f'{field} is required'}), 400
    
    try:
        recurring = RecurringBooking(
            client_id=current_user.id,
            worker_id=data['worker_id'],
            recurrence_type=data['recurrence_type'],
            recurrence_interval=data.get('recurrence_interval', 1),
            recurrence_days=json.dumps(data['recurrence_days']) if data.get('recurrence_days') else None,
            recurrence_day_of_month=data.get('recurrence_day_of_month'),
            start_time=datetime.strptime(data['start_time'], '%H:%M').time(),
            duration_minutes=data['duration_minutes'],
            service_type=data['service_type'],
            description=data.get('description'),
            location=data.get('location'),
            start_date=datetime.strptime(data['start_date'], '%Y-%m-%d').date(),
            end_date=datetime.strptime(data['end_date'], '%Y-%m-%d').date() if data.get('end_date') else None,
            estimated_cost_per_session=data.get('estimated_cost_per_session')
        )
        
        # Calculate and create first occurrence
        recurring.calculate_next_occurrence()
        if recurring.next_occurrence:
            first_booking = recurring.create_booking_for_date(recurring.next_occurrence)
            db.session.add(first_booking)
        
        db.session.add(recurring)
        db.session.commit()
        
        return jsonify({
            'success': True,
            'recurring_booking': recurring.to_dict()
        })
        
    except Exception as e:
        db.session.rollback()
        return jsonify({'error': str(e)}), 500


@scheduling_bp.route('/api/recurring-bookings/<int:recurring_id>', methods=['DELETE'])
@login_required
def cancel_recurring_booking(recurring_id):
    """Cancel a recurring booking series"""
    recurring = RecurringBooking.query.get_or_404(recurring_id)
    
    # Check authorization
    if recurring.client_id != current_user.id and recurring.worker_id != current_user.id:
        return jsonify({'error': 'Unauthorized'}), 403
    
    recurring.is_active = False
    
    # Cancel all future bookings in this series
    future_bookings = Booking.query.filter(
        Booking.recurring_booking_id == recurring_id,
        Booking.booking_date >= datetime.now().date(),
        Booking.status.in_([BookingStatus.PENDING, BookingStatus.CONFIRMED])
    ).all()
    
    for booking in future_bookings:
        booking.cancel("Recurring series cancelled")
    
    db.session.commit()
    
    return jsonify({
        'success': True,
        'message': f'Cancelled recurring booking and {len(future_bookings)} future bookings'
    })


def check_availability(worker_id, date, start_time, end_time):
    """Check if worker is available for booking"""
    # Check for existing bookings
    existing_bookings = Booking.query.filter(
        Booking.worker_id == worker_id,
        Booking.booking_date == date,
        Booking.status.in_([BookingStatus.CONFIRMED, BookingStatus.IN_PROGRESS])
    ).all()
    
    for booking in existing_bookings:
        # Check for time overlap
        if (start_time < booking.end_time and end_time > booking.start_time):
            return {
                'available': False,
                'reason': 'Worker already has a booking at this time'
            }
    
    # Check availability slots
    availability_slots = Availability.query.filter(
        Availability.worker_id == worker_id,
        Availability.date == date,
        Availability.status == AvailabilityStatus.AVAILABLE
    ).all()
    
    for slot in availability_slots:
        if start_time >= slot.start_time and end_time <= slot.end_time:
            if slot.is_available():
                return {
                    'available': True,
                    'availability_id': slot.id,
                    'availability_slot': slot
                }
    
    # No matching availability slot found
    return {
        'available': False,
        'reason': 'Worker is not available at this time'
    }


@scheduling_bp.route('/api/workers/<int:worker_id>/availability', methods=['GET'])
@login_required
def get_worker_availability(worker_id):
    """Get a worker's availability for booking"""
    start_date = request.args.get('start_date')
    end_date = request.args.get('end_date')
    
    if not start_date or not end_date:
        return jsonify({'error': 'Start and end dates required'}), 400
    
    try:
        start = datetime.strptime(start_date, '%Y-%m-%d').date()
        end = datetime.strptime(end_date, '%Y-%m-%d').date()
    except ValueError:
        return jsonify({'error': 'Invalid date format'}), 400
    
    # Get available slots
    availability = Availability.query.filter(
        Availability.worker_id == worker_id,
        Availability.date >= start,
        Availability.date <= end,
        Availability.status == AvailabilityStatus.AVAILABLE
    ).all()
    
    # Get existing bookings
    bookings = Booking.query.filter(
        Booking.worker_id == worker_id,
        Booking.booking_date >= start,
        Booking.booking_date <= end,
        Booking.status.in_([BookingStatus.CONFIRMED, BookingStatus.IN_PROGRESS])
    ).all()
    
    available_slots = []
    for slot in availability:
        if slot.is_available():
            # Check for booking conflicts
            has_conflict = False
            for booking in bookings:
                if booking.booking_date == slot.date:
                    if (booking.start_time < slot.end_time and 
                        booking.end_time > slot.start_time):
                        has_conflict = True
                        break
            
            if not has_conflict:
                available_slots.append({
                    'date': slot.date.isoformat(),
                    'start_time': slot.start_time.isoformat(),
                    'end_time': slot.end_time.isoformat(),
                    'availability_id': slot.id
                })
    
    return jsonify({
        'success': True,
        'available_slots': available_slots
    })
