from datetime import datetime
from decimal import Decimal
from app import db

class Project(db.Model):
    """Project model for client projects with billing information"""
    
    __tablename__ = 'projects'
    
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(200), nullable=False, index=True)
    client_id = db.Column(db.Integer, db.ForeignKey('clients.id'), nullable=False, index=True)
    description = db.Column(db.Text, nullable=True)
    billable = db.Column(db.Boolean, default=True, nullable=False)
    hourly_rate = db.Column(db.Numeric(9, 2), nullable=True)
    billing_ref = db.Column(db.String(100), nullable=True)
    status = db.Column(db.String(20), default='active', nullable=False)  # 'active' or 'archived'
    created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
    
    # Relationships
    time_entries = db.relationship('TimeEntry', backref='project', lazy='dynamic', cascade='all, delete-orphan')
    tasks = db.relationship('Task', backref='project', lazy='dynamic', cascade='all, delete-orphan')
    # comments relationship is defined via backref in Comment model
    
    def __init__(self, name, client_id=None, description=None, billable=True, hourly_rate=None, billing_ref=None, client=None):
        """Create a Project.

        Backward-compatible initializer that accepts either client_id or client name.
        If client name is provided and client_id is not, the corresponding Client
        record will be found or created on the fly and client_id will be set.
        """
        from .client import Client  # local import to avoid circular dependencies

        self.name = name.strip()
        self.description = description.strip() if description else None
        self.billable = billable
        self.hourly_rate = Decimal(str(hourly_rate)) if hourly_rate else None
        self.billing_ref = billing_ref.strip() if billing_ref else None

        resolved_client_id = client_id
        if resolved_client_id is None and client:
            # Find or create client by name
            client_name = client.strip()
            existing = Client.query.filter_by(name=client_name).first()
            if existing:
                resolved_client_id = existing.id
            else:
                new_client = Client(name=client_name)
                db.session.add(new_client)
                # Flush to obtain id without committing the whole transaction
                try:
                    db.session.flush()
                    resolved_client_id = new_client.id
                except Exception:
                    # If flush fails, fallback to committing
                    db.session.commit()
                    resolved_client_id = new_client.id

        self.client_id = resolved_client_id
    
    def __repr__(self):
        return f'<Project {self.name} ({self.client_obj.name if self.client_obj else "Unknown Client"})>'
    
    @property
    def client(self):
        """Get client name for backward compatibility"""
        return self.client_obj.name if self.client_obj else "Unknown Client"
    
    @property
    def is_active(self):
        """Check if project is active"""
        return self.status == 'active'
    
    @property
    def total_hours(self):
        """Calculate total hours spent on this project"""
        from .time_entry import TimeEntry
        total_seconds = db.session.query(
            db.func.sum(TimeEntry.duration_seconds)
        ).filter(
            TimeEntry.project_id == self.id,
            TimeEntry.end_time.isnot(None)
        ).scalar() or 0
        return round(total_seconds / 3600, 2)
    
    @property
    def total_billable_hours(self):
        """Calculate total billable hours spent on this project"""
        from .time_entry import TimeEntry
        total_seconds = db.session.query(
            db.func.sum(TimeEntry.duration_seconds)
        ).filter(
            TimeEntry.project_id == self.id,
            TimeEntry.end_time.isnot(None),
            TimeEntry.billable == True
        ).scalar() or 0
        return round(total_seconds / 3600, 2)
    
    @property
    def estimated_cost(self):
        """Calculate estimated cost based on billable hours and hourly rate"""
        if not self.billable or not self.hourly_rate:
            return 0.0
        return float(self.total_billable_hours) * float(self.hourly_rate)
    
    def get_entries_by_user(self, user_id=None, start_date=None, end_date=None):
        """Get time entries for this project, optionally filtered by user and date range"""
        from .time_entry import TimeEntry
        query = self.time_entries.filter(TimeEntry.end_time.isnot(None))
        
        if user_id:
            query = query.filter(TimeEntry.user_id == user_id)
        
        if start_date:
            query = query.filter(TimeEntry.start_time >= start_date)
        
        if end_date:
            query = query.filter(TimeEntry.start_time <= end_date)
        
        return query.order_by(TimeEntry.start_time.desc()).all()
    
    def get_user_totals(self, start_date=None, end_date=None):
        """Get total hours per user for this project"""
        from .time_entry import TimeEntry
        from .user import User
        
        query = db.session.query(
            User.id,
            User.username,
            User.full_name,
            db.func.sum(TimeEntry.duration_seconds).label('total_seconds')
        ).join(TimeEntry).filter(
            TimeEntry.project_id == self.id,
            TimeEntry.end_time.isnot(None)
        )
        
        if start_date:
            query = query.filter(TimeEntry.start_time >= start_date)
        
        if end_date:
            query = query.filter(TimeEntry.start_time <= end_date)
        
        results = query.group_by(User.id, User.username, User.full_name).all()
        
        return [
            {
                'username': (full_name.strip() if full_name and full_name.strip() else username),
                'total_hours': round(total_seconds / 3600, 2)
            }
            for _id, username, full_name, total_seconds in results
        ]
    
    def archive(self):
        """Archive the project"""
        self.status = 'archived'
        self.updated_at = datetime.utcnow()
        db.session.commit()
    
    def unarchive(self):
        """Unarchive the project"""
        self.status = 'active'
        self.updated_at = datetime.utcnow()
        db.session.commit()
    
    def to_dict(self):
        """Convert project to dictionary for API responses"""
        return {
            'id': self.id,
            'name': self.name,
            'client': self.client,
            'description': self.description,
            'billable': self.billable,
            'hourly_rate': float(self.hourly_rate) if self.hourly_rate else None,
            'billing_ref': self.billing_ref,
            'status': self.status,
            'created_at': self.created_at.isoformat() if self.created_at else None,
            'updated_at': self.updated_at.isoformat() if self.updated_at else None,
            'total_hours': self.total_hours,
            'total_billable_hours': self.total_billable_hours,
            'estimated_cost': float(self.estimated_cost) if self.estimated_cost else None
        }
