from datetime import datetime, timezone, timedelta

from app.extensions import db

# Lock expires after 5 minutes without heartbeat
LOCK_TIMEOUT_MINUTES = 5


class FileLock(db.Model):
    __tablename__ = 'file_locks'

    id = db.Column(db.Integer, primary_key=True)
    file_id = db.Column(db.Integer, db.ForeignKey('files.id'), unique=True, nullable=False, index=True)
    locked_by = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    locked_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc), nullable=False)
    heartbeat_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc), nullable=False)
    client_info = db.Column(db.String(255), nullable=True)  # e.g. "Desktop-Client Windows"

    file = db.relationship('File', backref=db.backref('lock', uselist=False))
    user = db.relationship('User', backref='file_locks')

    def is_expired(self):
        cutoff = datetime.now(timezone.utc) - timedelta(minutes=LOCK_TIMEOUT_MINUTES)
        return self.heartbeat_at.replace(tzinfo=timezone.utc) < cutoff

    def to_dict(self):
        return {
            'id': self.id,
            'file_id': self.file_id,
            'locked_by': self.locked_by,
            'locked_by_username': self.user.username if self.user else None,
            'locked_at': self.locked_at.isoformat() if self.locked_at else None,
            'heartbeat_at': self.heartbeat_at.isoformat() if self.heartbeat_at else None,
            'client_info': self.client_info,
            'is_expired': self.is_expired(),
        }

    @staticmethod
    def cleanup_expired():
        """Remove all expired locks."""
        cutoff = datetime.now(timezone.utc) - timedelta(minutes=LOCK_TIMEOUT_MINUTES)
        expired = FileLock.query.filter(FileLock.heartbeat_at < cutoff).all()
        count = len(expired)
        for lock in expired:
            db.session.delete(lock)
        if count:
            db.session.commit()
        return count

    @staticmethod
    def get_lock(file_id):
        """Get active (non-expired) lock for a file, cleaning up expired ones."""
        lock = FileLock.query.filter_by(file_id=file_id).first()
        if lock and lock.is_expired():
            db.session.delete(lock)
            db.session.commit()
            return None
        return lock
