"""
Time Tracking Model for Work Hours Management
"""

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


class TimeEntry(BaseModel):
    """Model for tracking time spent on jobs"""
    __tablename__ = 'time_entries'
    
    # Relationships
    worker_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    job_id = db.Column(db.Integer, db.ForeignKey('jobs.id'))
    booking_id = db.Column(db.Integer, db.ForeignKey('bookings.id'))
    contract_id = db.Column(db.Integer, db.ForeignKey('contracts.id'))
    
    worker = db.relationship('User', backref='time_entries', foreign_keys=[worker_id])
    job = db.relationship('Job', backref='time_entries')
    booking = db.relationship('Booking', backref='time_entries')
    contract = db.relationship('Contract', backref='time_entries')
    
    # Time tracking
    clock_in = db.Column(db.DateTime, nullable=False)
    clock_out = db.Column(db.DateTime)
    break_start = db.Column(db.DateTime)
    break_end = db.Column(db.DateTime)
    total_break_minutes = db.Column(db.Integer, default=0)
    
    # Calculated fields
    total_minutes = db.Column(db.Integer)
    regular_hours = db.Column(db.Float)
    overtime_hours = db.Column(db.Float)
    
    # Work details
    work_date = db.Column(db.Date, nullable=False)
    description = db.Column(db.Text)
    location = db.Column(db.String(255))
    
    # Approval
    is_approved = db.Column(db.Boolean, default=False)
    approved_by = db.Column(db.Integer, db.ForeignKey('users.id'))
    approved_at = db.Column(db.DateTime)
    approver = db.relationship('User', backref='approved_time_entries', foreign_keys=[approved_by])
    
    # Manual entry flag
    is_manual_entry = db.Column(db.Boolean, default=False)
    
    # Notes
    notes = db.Column(db.Text)
    
    # Indexes for faster queries
    __table_args__ = (
        Index('idx_time_entry_worker_date', 'worker_id', 'work_date'),
        Index('idx_time_entry_job', 'job_id'),
        Index('idx_time_entry_booking', 'booking_id'),
    )
    
    def clock_in_now(self):
        """Clock in for work"""
        if not self.clock_in:
            self.clock_in = datetime.utcnow()
            return True
        return False
    
    def clock_out_now(self):
        """Clock out from work"""
        if self.clock_in and not self.clock_out:
            self.clock_out = datetime.utcnow()
            self.calculate_total_time()
            return True
        return False
    
    def start_break(self):
        """Start a break"""
        if self.clock_in and not self.clock_out and not self.break_start:
            self.break_start = datetime.utcnow()
            return True
        return False
    
    def end_break(self):
        """End a break"""
        if self.break_start and not self.break_end:
            self.break_end = datetime.utcnow()
            break_duration = (self.break_end - self.break_start).total_seconds() / 60
            self.total_break_minutes = (self.total_break_minutes or 0) + int(break_duration)
            self.break_start = None
            self.break_end = None
            return True
        return False
    
    def calculate_total_time(self):
        """Calculate total worked time"""
        if self.clock_in and self.clock_out:
            total_seconds = (self.clock_out - self.clock_in).total_seconds()
            total_minutes = int(total_seconds / 60)
            
            # Subtract break time
            total_minutes -= (self.total_break_minutes or 0)
            
            self.total_minutes = max(0, total_minutes)
            
            # Calculate regular and overtime hours (8 hours = regular)
            total_hours = self.total_minutes / 60.0
            if total_hours > 8:
                self.regular_hours = 8.0
                self.overtime_hours = total_hours - 8.0
            else:
                self.regular_hours = total_hours
                self.overtime_hours = 0.0
            
            return self.total_minutes
        return 0
    
    def approve(self, approver_id):
        """Approve the time entry"""
        if not self.is_approved:
            self.is_approved = True
            self.approved_by = approver_id
            self.approved_at = datetime.utcnow()
            return True
        return False
    
    def to_dict(self):
        """Convert to dictionary"""
        return {
            'id': self.id,
            'worker_id': self.worker_id,
            'job_id': self.job_id,
            'booking_id': self.booking_id,
            'contract_id': self.contract_id,
            'clock_in': self.clock_in.isoformat() if self.clock_in else None,
            'clock_out': self.clock_out.isoformat() if self.clock_out else None,
            'total_break_minutes': self.total_break_minutes,
            'total_minutes': self.total_minutes,
            'regular_hours': self.regular_hours,
            'overtime_hours': self.overtime_hours,
            'work_date': self.work_date.isoformat() if self.work_date else None,
            'description': self.description,
            'location': self.location,
            'is_approved': self.is_approved,
            'is_manual_entry': self.is_manual_entry,
            'notes': self.notes
        }


class Shift(BaseModel):
    """Model for work shifts"""
    __tablename__ = 'shifts'
    
    # Relationships
    worker_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    job_id = db.Column(db.Integer, db.ForeignKey('jobs.id'))
    
    worker = db.relationship('User', backref='shifts', foreign_keys=[worker_id])
    job = db.relationship('Job', backref='shifts')
    
    # Shift details
    shift_date = db.Column(db.Date, nullable=False)
    start_time = db.Column(db.Time, nullable=False)
    end_time = db.Column(db.Time, nullable=False)
    
    # Shift type
    shift_type = db.Column(db.String(50))  # morning, afternoon, night, custom
    shift_name = db.Column(db.String(100))
    
    # Assignment
    is_open = db.Column(db.Boolean, default=True)  # Available for bidding
    min_workers_required = db.Column(db.Integer, default=1)
    max_workers_allowed = db.Column(db.Integer, default=1)
    current_workers = db.Column(db.Integer, default=0)
    
    # Requirements
    required_skills = db.Column(db.Text)  # JSON array of required skills
    min_rating = db.Column(db.Float)  # Minimum worker rating required
    
    # Compensation
    hourly_rate = db.Column(db.Float)
    overtime_rate = db.Column(db.Float)
    
    # Status
    status = db.Column(db.String(20), default='open')  # open, filled, in_progress, completed
    
    # Notes
    description = db.Column(db.Text)
    special_instructions = db.Column(db.Text)
    
    # Indexes
    __table_args__ = (
        Index('idx_shift_date_status', 'shift_date', 'status'),
        Index('idx_shift_worker', 'worker_id', 'shift_date'),
    )
    
    def to_dict(self):
        """Convert to dictionary"""
        return {
            'id': self.id,
            'worker_id': self.worker_id,
            'job_id': self.job_id,
            'shift_date': self.shift_date.isoformat() if self.shift_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,
            'shift_type': self.shift_type,
            'shift_name': self.shift_name,
            'is_open': self.is_open,
            'min_workers_required': self.min_workers_required,
            'max_workers_allowed': self.max_workers_allowed,
            'current_workers': self.current_workers,
            'required_skills': json.loads(self.required_skills) if self.required_skills else [],
            'min_rating': self.min_rating,
            'hourly_rate': self.hourly_rate,
            'overtime_rate': self.overtime_rate,
            'status': self.status,
            'description': self.description
        }
    
    def can_assign_worker(self):
        """Check if more workers can be assigned"""
        return self.is_open and self.current_workers < self.max_workers_allowed
    
    def assign_worker(self, worker_id):
        """Assign a worker to the shift"""
        if self.can_assign_worker():
            self.worker_id = worker_id
            self.current_workers += 1
            if self.current_workers >= self.min_workers_required:
                self.status = 'filled'
            if self.current_workers >= self.max_workers_allowed:
                self.is_open = False
            return True
        return False
    
    def unassign_worker(self):
        """Remove worker from shift"""
        if self.worker_id:
            self.worker_id = None
            self.current_workers = max(0, self.current_workers - 1)
            if self.current_workers < self.min_workers_required:
                self.status = 'open'
                self.is_open = True
            return True
        return False


class ShiftBid(BaseModel):
    """Model for shift bidding system"""
    __tablename__ = 'shift_bids'
    
    # Relationships
    shift_id = db.Column(db.Integer, db.ForeignKey('shifts.id'), nullable=False)
    worker_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    
    shift = db.relationship('Shift', backref='bids')
    worker = db.relationship('User', backref='shift_bids', foreign_keys=[worker_id])
    
    # Bid details
    bid_amount = db.Column(db.Float)  # Optional: worker's rate bid
    availability_confirmed = db.Column(db.Boolean, default=True)
    
    # Status
    status = db.Column(db.String(20), default='pending')  # pending, accepted, rejected
    
    # Selection
    selected_at = db.Column(db.DateTime)
    selected_by = db.Column(db.Integer, db.ForeignKey('users.id'))
    selector = db.relationship('User', backref='selected_shift_bids', foreign_keys=[selected_by])
    
    # Notes
    worker_notes = db.Column(db.Text)
    rejection_reason = db.Column(db.Text)
    
    # Unique constraint to prevent duplicate bids
    __table_args__ = (
        db.UniqueConstraint('shift_id', 'worker_id', name='_shift_worker_uc'),
        Index('idx_shift_bid_status', 'shift_id', 'status'),
    )
    
    def accept(self, selector_id):
        """Accept the bid"""
        if self.status == 'pending' and self.shift.can_assign_worker():
            self.status = 'accepted'
            self.selected_at = datetime.utcnow()
            self.selected_by = selector_id
            self.shift.assign_worker(self.worker_id)
            return True
        return False
    
    def reject(self, reason=None):
        """Reject the bid"""
        if self.status == 'pending':
            self.status = 'rejected'
            self.rejection_reason = reason
            return True
        return False


class ShiftSwapRequest(BaseModel):
    """Model for shift swapping between workers"""
    __tablename__ = 'shift_swap_requests'
    
    # Original shift holder
    from_worker_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    from_shift_id = db.Column(db.Integer, db.ForeignKey('shifts.id'), nullable=False)
    
    # Requested swap target
    to_worker_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    to_shift_id = db.Column(db.Integer, db.ForeignKey('shifts.id'))
    
    from_worker = db.relationship('User', backref='swap_requests_sent', foreign_keys=[from_worker_id])
    to_worker = db.relationship('User', backref='swap_requests_received', foreign_keys=[to_worker_id])
    from_shift = db.relationship('Shift', foreign_keys=[from_shift_id])
    to_shift = db.relationship('Shift', foreign_keys=[to_shift_id])
    
    # Request details
    reason = db.Column(db.Text)
    is_permanent = db.Column(db.Boolean, default=False)
    
    # Status
    status = db.Column(db.String(20), default='pending')  # pending, accepted, rejected, approved
    
    # Approvals
    worker_approved = db.Column(db.Boolean, default=False)
    manager_approved = db.Column(db.Boolean, default=False)
    approved_by = db.Column(db.Integer, db.ForeignKey('users.id'))
    approved_at = db.Column(db.DateTime)
    
    approver = db.relationship('User', backref='approved_swaps', foreign_keys=[approved_by])
    
    def approve_by_worker(self):
        """Approve swap by the target worker"""
        if self.status == 'pending' and self.to_worker_id:
            self.worker_approved = True
            self.status = 'accepted'
            return True
        return False
    
    def approve_by_manager(self, manager_id):
        """Final approval by manager"""
        if self.worker_approved and not self.manager_approved:
            self.manager_approved = True
            self.approved_by = manager_id
            self.approved_at = datetime.utcnow()
            self.status = 'approved'
            
            # Perform the swap
            if self.from_shift and self.to_worker_id:
                self.from_shift.worker_id = self.to_worker_id
                if self.to_shift and self.from_worker_id:
                    self.to_shift.worker_id = self.from_worker_id
            
            return True
        return False
