from flask import request, jsonify
from app import db
from app.api import bp
from app.models import PrintJob, Printer, User
from app.auth.routes import token_required, admin_required
from datetime import datetime, timedelta

@bp.route('/jobs', methods=['GET'])
@token_required
def get_jobs():
    """Get jobs for the current user or all jobs for admin"""
    is_admin = request.user_role == 'admin'
    user_id = request.user_id
    
    # Parse query parameters
    status = request.args.get('status')  # active, upcoming, completed, aborted, all
    printer_id = request.args.get('printer_id')
    
    # Base query
    query = PrintJob.query
    
    # Filter by user unless admin
    if not is_admin:
        query = query.filter_by(user_id=user_id)
    
    # Filter by printer if provided
    if printer_id:
        query = query.filter_by(printer_id=printer_id)
    
    # Apply status filter
    now = datetime.utcnow()
    if status == 'active':
        query = query.filter_by(aborted=False) \
            .filter(PrintJob.start_at <= now) \
            .filter(PrintJob.start_at.op('+')(PrintJob.duration_in_minutes * 60) > now)
    elif status == 'upcoming':
        query = query.filter_by(aborted=False) \
            .filter(PrintJob.start_at > now)
    elif status == 'completed':
        query = query.filter_by(aborted=False) \
            .filter(PrintJob.start_at.op('+')(PrintJob.duration_in_minutes * 60) <= now)
    elif status == 'aborted':
        query = query.filter_by(aborted=True)
    
    # Order by start time, most recent first
    query = query.order_by(PrintJob.start_at.desc())
    
    # Execute query
    jobs = query.all()
    result = [job.to_dict() for job in jobs]
    
    return jsonify(result)

@bp.route('/jobs/<job_id>', methods=['GET'])
@token_required
def get_job(job_id):
    """Get a specific job"""
    job = PrintJob.query.get_or_404(job_id)
    
    # Check permissions
    is_admin = request.user_role == 'admin'
    user_id = request.user_id
    
    if not is_admin and job.user_id != user_id:
        return jsonify({'error': 'Not authorized to view this job'}), 403
    
    return jsonify(job.to_dict())

@bp.route('/jobs', methods=['POST'])
@token_required
def create_job():
    """Create a new print job (reserve a printer)"""
    data = request.get_json() or {}
    
    required_fields = ['printer_id', 'start_at', 'duration_in_minutes']
    for field in required_fields:
        if field not in data:
            return jsonify({'error': f'Missing required field: {field}'}), 400
    
    # Validate printer
    printer = Printer.query.get(data['printer_id'])
    if not printer:
        return jsonify({'error': 'Printer not found'}), 404
    
    if printer.status != 0:  # Not operational
        return jsonify({'error': 'Printer is not operational'}), 400
    
    # Parse start time
    try:
        start_at = datetime.fromisoformat(data['start_at'].replace('Z', '+00:00'))
    except ValueError:
        return jsonify({'error': 'Invalid start_at format'}), 400
    
    # Validate duration
    try:
        duration = int(data['duration_in_minutes'])
        if duration <= 0 or duration > 480:  # Max 8 hours
            return jsonify({'error': 'Invalid duration (must be between 1 and 480 minutes)'}), 400
    except ValueError:
        return jsonify({'error': 'Duration must be a number'}), 400
    
    end_at = start_at + timedelta(minutes=duration)
    
    # Check if the printer is available during the requested time
    conflicting_jobs = PrintJob.query.filter_by(printer_id=printer.id, aborted=False) \
        .filter(
            (PrintJob.start_at < end_at) & 
            (PrintJob.start_at.op('+')(PrintJob.duration_in_minutes * 60) > start_at)
        ) \
        .all()
    
    if conflicting_jobs:
        return jsonify({'error': 'Printer is not available during the requested time'}), 409
    
    # Create job
    job = PrintJob(
        printer_id=data['printer_id'],
        user_id=request.user_id,
        start_at=start_at,
        duration_in_minutes=duration,
        comments=data.get('comments', '')
    )
    
    db.session.add(job)
    db.session.commit()
    
    return jsonify(job.to_dict()), 201

@bp.route('/jobs/<job_id>', methods=['PUT'])
@token_required
def update_job(job_id):
    """Update a job"""
    job = PrintJob.query.get_or_404(job_id)
    
    # Check permissions
    is_admin = request.user_role == 'admin'
    user_id = request.user_id
    
    if not is_admin and job.user_id != user_id:
        return jsonify({'error': 'Not authorized to update this job'}), 403
    
    data = request.get_json() or {}
    
    # Only allow certain fields to be updated
    if 'comments' in data:
        job.comments = data['comments']
    
    # Admin or owner can abort a job
    if 'aborted' in data and data['aborted'] and not job.aborted:
        job.aborted = True
        job.abort_reason = data.get('abort_reason', '')
    
    # Admin or owner can extend a job if it's active
    now = datetime.utcnow()
    is_active = (not job.aborted and 
                job.start_at <= now and 
                job.get_end_time() > now)
    
    if 'extend_minutes' in data and is_active:
        try:
            extend_minutes = int(data['extend_minutes'])
            if extend_minutes <= 0 or extend_minutes > 120:  # Max extend 2 hours
                return jsonify({'error': 'Invalid extension (must be between 1 and 120 minutes)'}), 400
            
            new_end_time = job.get_end_time() + timedelta(minutes=extend_minutes)
            
            # Check for conflicts with the extension
            conflicting_jobs = PrintJob.query.filter_by(printer_id=job.printer_id, aborted=False) \
                .filter(PrintJob.id != job.id) \
                .filter(PrintJob.start_at < new_end_time) \
                .filter(PrintJob.start_at > job.get_end_time()) \
                .all()
            
            if conflicting_jobs:
                return jsonify({'error': 'Cannot extend job due to conflicts with other reservations'}), 409
            
            job.duration_in_minutes += extend_minutes
        except ValueError:
            return jsonify({'error': 'Extend minutes must be a number'}), 400
    
    db.session.commit()
    
    return jsonify(job.to_dict())

@bp.route('/jobs/<job_id>', methods=['DELETE'])
@token_required
def delete_job(job_id):
    """Delete a job (cancel reservation)"""
    job = PrintJob.query.get_or_404(job_id)
    
    # Check permissions
    is_admin = request.user_role == 'admin'
    user_id = request.user_id
    
    if not is_admin and job.user_id != user_id:
        return jsonify({'error': 'Not authorized to delete this job'}), 403
    
    # Only allow deletion of upcoming jobs
    now = datetime.utcnow()
    if job.start_at <= now and not is_admin:
        return jsonify({'error': 'Cannot delete an active or completed job'}), 400
    
    db.session.delete(job)
    db.session.commit()
    
    return jsonify({'message': 'Job deleted successfully'})

@bp.route('/jobs/<job_id>/remaining-time', methods=['GET'])
def get_remaining_time(job_id):
    """Get remaining time for a job (public endpoint)"""
    job = PrintJob.query.get_or_404(job_id)
    
    remaining_seconds = job.get_remaining_time()
    
    return jsonify({
        'job_id': job.id,
        'remaining_seconds': remaining_seconds,
        'is_active': job.is_active()
    })