"""
Booking Model for Job Scheduling
"""

from datetime import datetime, timedelta
from sqlalchemy import Index
from app.extensions import db
from app.models.base import BaseModel
import json


class BookingStatus:
    """Booking status constants"""
    PENDING = 'pending'
    CONFIRMED = 'confirmed'
    IN_PROGRESS = 'in_progress'
    COMPLETED = 'completed'
    CANCELLED = 'cancelled'
    NO_SHOW = 'no_show'


class Booking(BaseModel):
    """Model for job bookings"""
    __tablename__ = 'bookings'
    
    # Relationships
    client_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    worker_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    job_id = db.Column(db.Integer, db.ForeignKey('jobs.id'))
    availability_id = db.Column(db.Integer, db.ForeignKey('availability.id'))
    
    client = db.relationship('User', backref='client_bookings', foreign_keys=[client_id])
    worker = db.relationship('User', backref='worker_bookings', foreign_keys=[worker_id])
    job = db.relationship('Job', backref='bookings')
    availability_slot = db.relationship('Availability', backref='bookings')
    
    # Booking details
    booking_date = db.Column(db.Date, nullable=False)
    start_time = db.Column(db.Time, nullable=False)
    end_time = db.Column(db.Time, nullable=False)
    duration_minutes = db.Column(db.Integer, nullable=False)
    
    # Service details
    service_type = db.Column(db.String(100), nullable=False)
    description = db.Column(db.Text)
    location = db.Column(db.String(255))
    
    # Status and tracking
    status = db.Column(db.String(20), default=BookingStatus.PENDING, nullable=False)
    confirmation_code = db.Column(db.String(20), unique=True)
    confirmed_at = db.Column(db.DateTime)
    cancelled_at = db.Column(db.DateTime)
    cancellation_reason = db.Column(db.Text)
    
    # Pricing
    estimated_cost = db.Column(db.Float)
    final_cost = db.Column(db.Float)
    
    # Reminders
    reminder_sent = db.Column(db.Boolean, default=False)
    reminder_sent_at = db.Column(db.DateTime)
    
    # Recurring booking reference
    recurring_booking_id = db.Column(db.Integer, db.ForeignKey('recurring_bookings.id'))
    
    # Notes
    client_notes = db.Column(db.Text)
    worker_notes = db.Column(db.Text)
    
    # Calendar integration
    google_event_id = db.Column(db.String(255))
    outlook_event_id = db.Column(db.String(255))
    
    # Indexes for faster queries
    __table_args__ = (
        Index('idx_booking_worker_date', 'worker_id', 'booking_date'),
        Index('idx_booking_client_date', 'client_id', 'booking_date'),
        Index('idx_booking_date_status', 'booking_date', 'status'),
        Index('idx_booking_confirmation', 'confirmation_code'),
    )
    
    def to_dict(self):
        """Convert to dictionary for API responses"""
        return {
            'id': self.id,
            'client_id': self.client_id,
            'worker_id': self.worker_id,
            'job_id': self.job_id,
            'booking_date': self.booking_date.isoformat() if self.booking_date else None,
            'start_time': self.start_time.isoformat() if self.start_time else None,
            'end_time': self.end_time.isoformat() if self.end_time else None,
            'duration_minutes': self.duration_minutes,
            'service_type': self.service_type,
            'description': self.description,
            'location': self.location,
            'status': self.status,
            'confirmation_code': self.confirmation_code,
            'estimated_cost': self.estimated_cost,
            'final_cost': self.final_cost,
            'client_notes': self.client_notes,
            'worker_notes': self.worker_notes,
            'created_at': self.created_at.isoformat() if self.created_at else None,
            'updated_at': self.updated_at.isoformat() if self.updated_at else None
        }
    
    def confirm(self):
        """Confirm the booking"""
        if self.status == BookingStatus.PENDING:
            self.status = BookingStatus.CONFIRMED
            self.confirmed_at = datetime.utcnow()
            return True
        return False
    
    def cancel(self, reason=None):
        """Cancel the booking"""
        if self.status in [BookingStatus.PENDING, BookingStatus.CONFIRMED]:
            self.status = BookingStatus.CANCELLED
            self.cancelled_at = datetime.utcnow()
            self.cancellation_reason = reason
            # Release the availability slot if linked
            if self.availability_slot:
                self.availability_slot.release_slot()
            return True
        return False
    
    def start(self):
        """Mark booking as in progress"""
        if self.status == BookingStatus.CONFIRMED:
            self.status = BookingStatus.IN_PROGRESS
            return True
        return False
    
    def complete(self):
        """Mark booking as completed"""
        if self.status == BookingStatus.IN_PROGRESS:
            self.status = BookingStatus.COMPLETED
            return True
        return False
    
    @staticmethod
    def generate_confirmation_code():
        """Generate unique confirmation code"""
        import random
        import string
        return ''.join(random.choices(string.ascii_uppercase + string.digits, k=8))


class RecurringBooking(BaseModel):
    """Model for recurring booking templates"""
    __tablename__ = 'recurring_bookings'
    
    # Relationships
    client_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    worker_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    
    client = db.relationship('User', backref='recurring_client_bookings', foreign_keys=[client_id])
    worker = db.relationship('User', backref='recurring_worker_bookings', foreign_keys=[worker_id])
    bookings = db.relationship('Booking', backref='recurring_booking')
    
    # Recurrence pattern
    recurrence_type = db.Column(db.String(20), nullable=False)  # daily, weekly, monthly, custom
    recurrence_interval = db.Column(db.Integer, default=1)  # Every X days/weeks/months
    recurrence_days = db.Column(db.Text)  # JSON array of weekday numbers for weekly recurrence
    recurrence_day_of_month = db.Column(db.Integer)  # For monthly recurrence
    
    # Time settings
    start_time = db.Column(db.Time, nullable=False)
    duration_minutes = db.Column(db.Integer, nullable=False)
    
    # Service details
    service_type = db.Column(db.String(100), nullable=False)
    description = db.Column(db.Text)
    location = db.Column(db.String(255))
    
    # Date range
    start_date = db.Column(db.Date, nullable=False)
    end_date = db.Column(db.Date)
    
    # Status
    is_active = db.Column(db.Boolean, default=True)
    
    # Pricing
    estimated_cost_per_session = db.Column(db.Float)
    
    # Next occurrence tracking
    next_occurrence = db.Column(db.Date)
    
    def to_dict(self):
        """Convert to dictionary"""
        return {
            'id': self.id,
            'client_id': self.client_id,
            'worker_id': self.worker_id,
            'recurrence_type': self.recurrence_type,
            'recurrence_interval': self.recurrence_interval,
            'recurrence_days': json.loads(self.recurrence_days) if self.recurrence_days else None,
            'recurrence_day_of_month': self.recurrence_day_of_month,
            'start_time': self.start_time.isoformat() if self.start_time else None,
            'duration_minutes': self.duration_minutes,
            'service_type': self.service_type,
            'description': self.description,
            'location': self.location,
            'start_date': self.start_date.isoformat() if self.start_date else None,
            'end_date': self.end_date.isoformat() if self.end_date else None,
            'is_active': self.is_active,
            'estimated_cost_per_session': self.estimated_cost_per_session,
            'next_occurrence': self.next_occurrence.isoformat() if self.next_occurrence else None
        }
    
    def calculate_next_occurrence(self, after_date=None):
        """Calculate the next occurrence date"""
        from datetime import date, timedelta
        
        if not self.is_active:
            return None
        
        base_date = after_date or date.today()
        
        if self.end_date and base_date > self.end_date:
            return None
        
        if self.recurrence_type == 'daily':
            next_date = base_date + timedelta(days=self.recurrence_interval)
        elif self.recurrence_type == 'weekly':
            # Find next matching weekday
            days = json.loads(self.recurrence_days) if self.recurrence_days else []
            if not days:
                return None
            
            current_weekday = base_date.weekday()
            days_ahead = None
            
            for day in sorted(days):
                if day > current_weekday:
                    days_ahead = day - current_weekday
                    break
            
            if days_ahead is None:
                # Next occurrence is next week
                days_ahead = (7 * self.recurrence_interval) - current_weekday + min(days)
            
            next_date = base_date + timedelta(days=days_ahead)
        elif self.recurrence_type == 'monthly':
            # Calculate next month occurrence
            from calendar import monthrange
            
            if base_date.day >= self.recurrence_day_of_month:
                # Move to next month
                if base_date.month == 12:
                    next_date = date(base_date.year + 1, 1, self.recurrence_day_of_month)
                else:
                    next_month = base_date.month + self.recurrence_interval
                    next_year = base_date.year
                    if next_month > 12:
                        next_month -= 12
                        next_year += 1
                    
                    # Handle months with fewer days
                    max_day = monthrange(next_year, next_month)[1]
                    day = min(self.recurrence_day_of_month, max_day)
                    next_date = date(next_year, next_month, day)
            else:
                next_date = date(base_date.year, base_date.month, self.recurrence_day_of_month)
        else:
            return None
        
        if self.end_date and next_date > self.end_date:
            return None
        
        self.next_occurrence = next_date
        return next_date
    
    def create_booking_for_date(self, booking_date):
        """Create a booking instance for a specific date"""
        from app.models.booking import Booking
        
        booking = Booking(
            client_id=self.client_id,
            worker_id=self.worker_id,
            recurring_booking_id=self.id,
            booking_date=booking_date,
            start_time=self.start_time,
            end_time=(datetime.combine(booking_date, self.start_time) + 
                     timedelta(minutes=self.duration_minutes)).time(),
            duration_minutes=self.duration_minutes,
            service_type=self.service_type,
            description=self.description,
            location=self.location,
            estimated_cost=self.estimated_cost_per_session,
            status=BookingStatus.PENDING,
            confirmation_code=Booking.generate_confirmation_code()
        )
        
        return booking
