"""Planning Schedules Routes - GMAO Collège"""
from flask import Blueprint, render_template, request, redirect, url_for, flash
from flask_login import login_required, current_user
from ..extensions import db
from ..core.models.planning import (
    PreventiveTask, PreventiveTaskConsumable, ScheduledTask,
    Meter, MeterReading, Consumable, ConsumableUsage,
    TechnicianAvailability, AdminTask, ZoneAccessRule,
    WorkSchedule, CollegeClosure, PlanningDay, PlanningItem
)
from ..core.models.maintenance import Lot, LotTask, Intervention
from ..core.models.equipment import EquipmentCategory
from ..core.models.company import Company
from ..core.models.college import Room, RoomSchedule
from ..core.models.user import User
from datetime import datetime, date, time as dt_time

planning_bp = Blueprint('planning', __name__, url_prefix='/planning', template_folder='templates')


@planning_bp.route('/work-schedules', methods=['GET', 'POST'])
@login_required
def work_schedules():
    """Configuration simple des horaires de chaque technicien.

    Une ligne représente une journée ; la pause est facultative et sépare la
    matinée de l'après-midi dans le DayPlanner. Les utilisateurs ne peuvent
    modifier que leurs propres horaires, les administrateurs peuvent gérer
    l'équipe.
    """
    from datetime import datetime
    users = User.query.filter_by(is_active=True).order_by(User.full_name, User.username).all()
    target_id = request.values.get('user_id', type=int) or current_user.id
    if target_id != current_user.id and not current_user.is_admin():
        target_id = current_user.id
    target = db.session.get(User, target_id) or current_user
    year = request.values.get('academic_year', date.today().year, type=int)
    if request.method == 'POST':
        for day in range(7):
            def parse(field):
                raw = request.form.get(field)
                return datetime.strptime(raw, '%H:%M').time() if raw else None
            start, end = parse(f'start_{day}'), parse(f'end_{day}')
            lunch_start, lunch_end = parse(f'lunch_start_{day}'), parse(f'lunch_end_{day}')
            row = WorkSchedule.query.filter_by(user_id=target.id, day_of_week=day, academic_year=year).first()
            if not start or not end:
                if row:
                    row.is_active = False
                continue
            if end <= start or (lunch_start and (not lunch_end or not start <= lunch_start < lunch_end <= end)):
                flash(f'Horaires invalides pour le jour {day + 1}.', 'danger')
                return redirect(url_for('planning.work_schedules', user_id=target.id, academic_year=year))
            if not row:
                row = WorkSchedule(user_id=target.id, day_of_week=day, academic_year=year)
                db.session.add(row)
            row.start_time, row.end_time = start, end
            row.lunch_start, row.lunch_end, row.is_active = lunch_start, lunch_end, True
        db.session.commit()
        flash(f'Horaires de {target.full_name or target.username} enregistrés.', 'success')
    rows = {row.day_of_week: row for row in WorkSchedule.query.filter_by(user_id=target.id, academic_year=year, is_active=True).all()}
    return render_template('planning/work_schedules.html', users=users, target=target, rows=rows, year=year,
                           days=['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi', 'Dimanche'])


@planning_bp.route('/my-day')
@login_required
def my_day():
    """Vue quotidienne : une proposition, jamais une écriture automatique."""
    from datetime import timedelta
    from ..core.services.day_planner import DayPlanner

    raw_date = request.args.get('date')
    try:
        target_date = date.fromisoformat(raw_date) if raw_date else date.today()
    except ValueError:
        flash('La date demandée est invalide.', 'warning')
        target_date = date.today()
    candidates = DayPlanner.from_database(target_date, user_id=current_user.id)
    windows = DayPlanner.work_windows(target_date, user_id=current_user.id)
    alternatives = {
        (item.source_type, item.source_id): DayPlanner.alternative_slots(
            target_date, item, candidates, user_id=current_user.id
        )
        for item in candidates
        if item.status in ('à replanifier', 'proposé', 'conflit')
    }
    execution_states = {}
    from ..core.services.meter_analytics import visible_open_meter_alerts
    from ..core.services.c4_service import visible_open_c4_alerts
    informational_alerts = visible_open_meter_alerts(current_user) + visible_open_c4_alerts(current_user)
    for task in ScheduledTask.query.filter(ScheduledTask.scheduled_date == target_date).all():
        execution_states[task.id] = {
            'status': task.status,
            'realized': task.realized_minutes,
            'remaining': task.remaining_minutes,
        }
    return render_template(
        'planning/my_day.html',
        target_date=target_date,
        candidates=candidates,
        work_windows=windows,
        previous_date=target_date - timedelta(days=1),
        next_date=target_date + timedelta(days=1),
        hours_configured=bool(windows),
        alternatives=alternatives,
        execution_states=execution_states,
        informational_alerts=informational_alerts,
    )


@planning_bp.route('/my-day/reschedule', methods=['POST'])
@login_required
def my_day_reschedule():
    """Valide un seul créneau choisi par l'utilisateur."""
    from ..core.services.day_planner import DayPlanner
    from ..core.models.planning import ScheduledTask
    source_type = request.form.get('source_type', '')
    source_id = request.form.get('source_id', type=int)
    raw_date = request.form.get('date', '')
    raw_slot = request.form.get('slot', '')
    raw_start, raw_end = (raw_slot.split('|', 1) if '|' in raw_slot else ('', ''))
    try:
        target_date = date.fromisoformat(raw_date)
        start_time = dt_time.fromisoformat(raw_start)
        end_time = dt_time.fromisoformat(raw_end)
    except (TypeError, ValueError):
        flash('Le créneau choisi est invalide.', 'danger')
        return redirect(url_for('planning.my_day'))
    if not source_id or end_time <= start_time:
        flash('Le créneau choisi est invalide.', 'danger')
        return redirect(url_for('planning.my_day', date=target_date.isoformat()))
    candidates = DayPlanner.from_database(target_date, user_id=current_user.id)
    candidate = next((item for item in candidates if item.source_type == source_type and item.source_id == source_id), None)
    if not candidate or candidate.status == 'terminée':
        flash('Cette tâche n’est plus replanifiable.', 'warning')
        return redirect(url_for('planning.my_day', date=target_date.isoformat()))
    duration = int(candidate.duration_minutes or 1)
    if (end_time.hour * 60 + end_time.minute) - (start_time.hour * 60 + start_time.minute) != duration:
        flash('La durée choisie ne correspond pas à la durée de la tâche.', 'danger')
        return redirect(url_for('planning.my_day', date=target_date.isoformat()))
    windows = DayPlanner.work_windows(target_date, user_id=current_user.id)
    if not any(window.start <= start_time and end_time <= window.end for window in windows):
        flash('Ce créneau est en dehors des horaires de travail ou pendant la pause.', 'danger')
        return redirect(url_for('planning.my_day', date=target_date.isoformat()))
    others = [item for item in candidates if item is not candidate and item.proposed_start and item.proposed_end]
    if any(item.proposed_start < end_time and item.proposed_end > start_time for item in others):
        flash('Ce créneau est déjà pris par une autre tâche fixe ou proposée.', 'danger')
        return redirect(url_for('planning.my_day', date=target_date.isoformat()))
    if candidate.room_id:
        from ..core.services.room_planning import room_is_available
        if not room_is_available(candidate.room_id, datetime.combine(target_date, start_time), datetime.combine(target_date, end_time)):
            flash('La salle est occupée sur ce créneau.', 'danger')
            return redirect(url_for('planning.my_day', date=target_date.isoformat()))
    if source_type == 'scheduled_task':
        task = ScheduledTask.query.get(source_id)
        if not task or (task.assigned_to_id not in (None, current_user.id)):
            flash('Cette tâche n’est pas accessible.', 'danger')
            return redirect(url_for('planning.my_day', date=target_date.isoformat()))
        task.scheduled_date, task.scheduled_start, task.scheduled_end = target_date, start_time, end_time
        task.status = 'planned'
    elif source_type == 'intervention':
        task = Intervention.query.get(source_id)
        if not task or (task.technician_id not in (None, current_user.id) and task.assigned_to_id not in (None, current_user.id)):
            flash('Cette intervention n’est pas accessible.', 'danger')
            return redirect(url_for('planning.my_day', date=target_date.isoformat()))
        task.scheduled_date, task.scheduled_start, task.scheduled_end = target_date, start_time, end_time
    else:
        flash('Cette catégorie ne peut pas encore être replanifiée depuis Ma journée.', 'info')
        return redirect(url_for('planning.my_day', date=target_date.isoformat()))
    db.session.commit()
    flash('Le nouveau créneau a été enregistré.', 'success')
    return redirect(url_for('planning.my_day', date=target_date.isoformat()))


def _execution_redirect(task):
    return redirect(url_for('planning.my_day', date=(task.scheduled_date or date.today()).isoformat()))


@planning_bp.route('/scheduled/<int:task_id>/start', methods=['POST'])
@login_required
def scheduled_start(task_id):
    task = ScheduledTask.query.get_or_404(task_id)
    if task.assigned_to_id not in (None, current_user.id):
        flash('Cette tâche ne vous est pas attribuée.', 'danger')
        return _execution_redirect(task)
    from ..core.services.task_execution import start_task
    try:
        start_task(task, technician_id=current_user.id)
        db.session.commit()
        flash('Tâche démarrée.', 'success')
    except ValueError as exc:
        flash(str(exc), 'warning')
    return _execution_redirect(task)


@planning_bp.route('/scheduled/<int:task_id>/pause', methods=['POST'])
@login_required
def scheduled_pause(task_id):
    task = ScheduledTask.query.get_or_404(task_id)
    from ..core.services.task_execution import pause_task
    try:
        pause_task(task, reason=request.form.get('reason', 'autre'), note=request.form.get('note'))
        db.session.commit()
        flash('Tâche mise en pause ; le temps réalisé est conservé.', 'success')
    except ValueError as exc:
        flash(str(exc), 'warning')
    return _execution_redirect(task)


@planning_bp.route('/scheduled/<int:task_id>/resume', methods=['POST'])
@login_required
def scheduled_resume(task_id):
    task = ScheduledTask.query.get_or_404(task_id)
    from ..core.services.task_execution import resume_task
    try:
        resume_task(task, technician_id=current_user.id)
        db.session.commit()
        flash('Tâche reprise.', 'success')
    except ValueError as exc:
        flash(str(exc), 'warning')
    return _execution_redirect(task)


@planning_bp.route('/scheduled/<int:task_id>/complete', methods=['POST'])
@login_required
def scheduled_complete(task_id):
    task = ScheduledTask.query.get_or_404(task_id)
    from ..core.services.task_execution import complete_task
    try:
        complete_task(task)
        db.session.commit()
        flash('Tâche terminée.', 'success')
    except ValueError as exc:
        flash(str(exc), 'warning')
    return _execution_redirect(task)


@planning_bp.route('/rooms')
@login_required
def room_schedules():
    """Emplois du temps internes des salles, toutes provenances confondues."""
    from ..core.services.room_planning import active_room_schedules
    room_id = request.args.get('room_id', type=int)
    rooms = Room.query.order_by(Room.name).all()
    schedules = active_room_schedules(room_id) if room_id else []
    return render_template('planning/room_schedules.html', rooms=rooms, schedules=schedules, selected_room_id=room_id)


@planning_bp.route('/rooms/new', methods=['GET', 'POST'])
@login_required
def room_schedule_new():
    rooms = Room.query.order_by(Room.name).all()
    if request.method == 'POST':
        try:
            room_id = int(request.form['room_id'])
            week_start = date.fromisoformat(request.form['week_start'])
            day_of_week = int(request.form['day_of_week'])
            start_time = dt_time.fromisoformat(request.form['start_time'])
            end_time = dt_time.fromisoformat(request.form['end_time'])
        except (KeyError, TypeError, ValueError):
            flash('Salle, date et horaires sont obligatoires.', 'danger')
            return render_template('planning/room_schedule_form.html', rooms=rooms, schedule=None), 400
        if (start_time >= end_time or not Room.query.get(room_id)
                or day_of_week < 0 or day_of_week > 6):
            flash('Vérifiez la salle et l’ordre des horaires.', 'danger')
            return render_template('planning/room_schedule_form.html', rooms=rooms, schedule=None), 400
        schedule = RoomSchedule(
            room_id=room_id, week_start=week_start, day_of_week=day_of_week,
            start_time=start_time, end_time=end_time,
            subject=(request.form.get('subject') or '').strip() or None,
            teacher=(request.form.get('teacher') or '').strip() or None,
            class_name=(request.form.get('class_name') or '').strip() or None,
            course_name=(request.form.get('course_name') or '').strip() or None,
            event_type=request.form.get('event_type') or 'cours',
            source='manual',
            protected_from_sync=request.form.get('protected_from_sync') == 'on',
        )
        db.session.add(schedule)
        db.session.commit()
        flash('Créneau de salle enregistré.', 'success')
        return redirect(url_for('planning.room_schedules', room_id=room_id))
    return render_template('planning/room_schedule_form.html', rooms=rooms, schedule=None)


@planning_bp.route('/rooms/<int:id>/protect', methods=['POST'])
@login_required
def room_schedule_protect(id):
    schedule = RoomSchedule.query.get_or_404(id)
    schedule.protected_from_sync = request.form.get('protected_from_sync') == 'on'
    db.session.commit()
    flash('Protection du créneau mise à jour.', 'success')
    return redirect(url_for('planning.room_schedules', room_id=schedule.room_id))


@planning_bp.route('/rooms/<int:id>/disable', methods=['POST'])
@login_required
def room_schedule_disable(id):
    """Désactive explicitement un créneau sans supprimer son historique."""
    schedule = RoomSchedule.query.get_or_404(id)
    schedule.resolution_status = 'disabled'
    db.session.commit()
    flash('Créneau désactivé ; son historique est conservé.', 'success')
    return redirect(url_for('planning.room_schedules', room_id=schedule.room_id))


@planning_bp.route('/')
@login_required
def index():
    """Page principale de planification - calendrier sur 1 an."""
    from datetime import date, timedelta
    from calendar import monthrange
    from ..core.services.planning_service import PlanningService
    from ..core.models.planning import PlanningDay, PlanningItem, ClosureWorkDay, WorkSchedule, TimeEntry, PersonalLeave
    from ..core.models.settings import AppSettings
    from ..core.services.planning_service import get_french_public_holidays
    
    # Récupérer l'année demandée ou année courante
    mode = request.args.get('mode')
    saved_mode = AppSettings.get(f'planning_calendar_mode_{current_user.id}', 'calendar')
    if mode not in ('calendar', 'school'):
        mode = saved_mode if saved_mode in ('calendar', 'school') else 'calendar'
    else:
        AppSettings.set(f'planning_calendar_mode_{current_user.id}', mode)
    year = request.args.get('year', date.today().year, type=int)
    if mode == 'school' and 'year' not in request.args:
        year = date.today().year if date.today().month >= 9 else date.today().year - 1
    display_start = date(year, 9, 1) if mode == 'school' else date(year, 1, 1)
    display_end = date(year + 1, 8, 31) if mode == 'school' else date(year, 12, 31)
    
    # Préparer les données du calendrier. Le calendrier principal est la
    # source unique : il rassemble les interventions et les échéances
    # préventives qui n'ont pas encore été converties en intervention.
    months_data = []
    months_names = ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
                    'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre']
    
    # Récupérer les vacances
    closures = CollegeClosure.query.filter(
        CollegeClosure.start_date <= display_end,
        CollegeClosure.end_date >= display_start
    ).all()
    permanence_dates = {item.work_date for item in ClosureWorkDay.query.filter(ClosureWorkDay.work_date >= display_start, ClosureWorkDay.work_date <= display_end).all()}
    permanence_map = {item.work_date: item for item in ClosureWorkDay.query.filter(ClosureWorkDay.work_date >= display_start, ClosureWorkDay.work_date <= display_end).all()}
    assignments = {item.day_of_week: item for item in WorkSchedule.query.filter_by(user_id=current_user.id, is_active=True).all()}
    time_entries = {item.work_date: item for item in TimeEntry.query.filter_by(user_id=current_user.id).filter(TimeEntry.work_date >= display_start, TimeEntry.work_date <= display_end).all()}
    personal_leaves = PersonalLeave.query.filter(PersonalLeave.user_id == current_user.id, PersonalLeave.end_date >= display_start, PersonalLeave.start_date <= display_end).all()
    leave_dates = {leave.start_date + timedelta(days=offset) for leave in personal_leaves for offset in range((leave.end_date - leave.start_date).days + 1)}
    holiday_dates = {item[0] for item in get_french_public_holidays(year)}
    if mode == 'school':
        holiday_dates |= {item[0] for item in get_french_public_holidays(year + 1)}
    
    # Fonction pour vérifier si une date est en vacances
    def is_vacation(d):
        for c in closures:
            if c.start_date <= d <= c.end_date:
                return c.name
        return None
    
    # Récupérer les congés personnels
    leaves = TechnicianAvailability.query.filter(
        TechnicianAvailability.date >= display_start,
        TechnicianAvailability.date <= display_end
    ).all()
    leave_dates = {l.date: l for l in leaves}

    year_start = display_start
    year_end = display_end
    terminal_intervention_statuses = ('terminee', 'cloturee', 'annulee', 'ignoree')
    interventions = Intervention.query.filter(
        Intervention.scheduled_date >= year_start,
        Intervention.scheduled_date <= year_end,
        Intervention.is_deleted.is_(False),
        ~Intervention.status.in_(terminal_intervention_statuses),
    ).order_by(Intervention.scheduled_date, Intervention.scheduled_start).all()
    scheduled_tasks = ScheduledTask.query.filter(
        ScheduledTask.scheduled_date >= year_start,
        ScheduledTask.scheduled_date <= year_end,
        ScheduledTask.status.in_(('planned', 'in_progress', 'suspended', 'postponed')),
        # Une échéance convertie est déjà représentée par son intervention.
        ScheduledTask.intervention_id.is_(None),
    ).order_by(ScheduledTask.scheduled_date, ScheduledTask.scheduled_start).all()

    events_by_date = {}
    for intervention in interventions:
        events_by_date.setdefault(intervention.scheduled_date, []).append({
            'title': intervention.title or 'Intervention',
            'type': intervention.type or 'curatif',
            'status': intervention.status,
            'start_time': intervention.scheduled_start,
            'end_time': intervention.scheduled_end,
            'duration': intervention.estimated_duration,
            'room_name': intervention.room.name if intervention.room else None,
            'url': url_for('interventions.detail', id=intervention.id),
            'source': 'Intervention',
            'color': 'danger' if intervention.type == 'curatif' else 'primary',
        })
    for task in scheduled_tasks:
        title = 'Tâche préventive'
        if task.lot_task and task.lot_task.tache:
            title = task.lot_task.tache
        elif task.preventive_task and task.preventive_task.name:
            title = task.preventive_task.name
        elif task.equipment:
            title = f'Intervention sur {task.equipment.name}'
        room = task.room or (task.equipment.effective_room if task.equipment else None)
        events_by_date.setdefault(task.scheduled_date, []).append({
            'title': title,
            'type': 'preventive',
            'status': task.status,
            'start_time': task.scheduled_start,
            'end_time': task.scheduled_end,
            'duration': task.estimated_duration,
            'room_name': room.name if room else None,
            'url': url_for('planning.scheduled_detail', id=task.id),
            'source': 'Échéance préventive',
            'color': 'info',
        })
    
    month_order = list(range(9, 13)) + list(range(1, 9)) if mode == 'school' else list(range(1, 13))
    for month in month_order:
        actual_year = year if mode != 'school' or month >= 9 else year + 1
        # Nombre de jours dans le mois
        days_in_month = monthrange(actual_year, month)[1]
        
        # Jour de la semaine du premier jour (0=lundi, 6=dimanche)
        first_weekday = date(actual_year, month, 1).weekday()
        
        weeks = []
        month_planned_minutes = 0
        month_overtime_minutes = 0
        month_delay_minutes = 0
        week = [None] * first_weekday  # Cases vides avant le 1er jour
        
        for day in range(1, days_in_month + 1):
            d = date(actual_year, month, day)
            vacation = is_vacation(d)
            leave = d in leave_dates
            weekday = d.weekday()
            planned_minutes = 0
            permanence = permanence_map.get(d)
            if permanence:
                planned_minutes = max((permanence.end_time.hour * 60 + permanence.end_time.minute) - (permanence.start_time.hour * 60 + permanence.start_time.minute), 0)
                if permanence.lunch_start and permanence.lunch_end:
                    planned_minutes -= (permanence.lunch_end.hour * 60 + permanence.lunch_end.minute) - (permanence.lunch_start.hour * 60 + permanence.lunch_start.minute)
            elif not holiday_dates.__contains__(d) and not leave and not vacation and weekday < 5:
                assignment = assignments.get(weekday)
                if assignment:
                    source = assignment.template if assignment.template and assignment.template.is_active else assignment
                    planned_minutes = max((source.end_time.hour * 60 + source.end_time.minute) - (source.start_time.hour * 60 + source.start_time.minute), 0)
                    if source.lunch_start and source.lunch_end:
                        planned_minutes -= (source.lunch_end.hour * 60 + source.lunch_end.minute) - (source.lunch_start.hour * 60 + source.lunch_start.minute)
            entry = time_entries.get(d)
            actual_minutes = entry.actual_minutes if entry else 0
            month_planned_minutes += planned_minutes
            if entry and entry.entry_type == 'supplementaire':
                month_overtime_minutes += actual_minutes or 0
            elif entry and entry.entry_type == 'retard':
                month_delay_minutes += actual_minutes or 0
            fmt = lambda minutes: f"{minutes // 60}h{minutes % 60:02d}"
            deviation_hours = None
            deviation_class = None
            if entry and entry.entry_type == 'retard' and actual_minutes:
                deviation_hours, deviation_class = f"-{fmt(actual_minutes)}", 'text-danger'
            elif entry and entry.entry_type == 'supplementaire' and actual_minutes:
                deviation_hours, deviation_class = f"+{fmt(actual_minutes)}", 'text-success'
            
            week.append({
                'day': day,
                'date': d,
                'is_weekend': weekday >= 5,
                'is_vacation': vacation is not None,
                'vacation_name': vacation,
                'is_holiday': d in holiday_dates,
                'is_permanence': d in permanence_dates,
                'is_leave': leave,
                'leave_type': 'congé' if leave else None,
                'worked_hours': fmt(actual_minutes),
                'planned_hours': fmt(planned_minutes),
                'deviation_hours': deviation_hours,
                'deviation_class': deviation_class,
                'events': events_by_date.get(d, []),
            })
            
            # Si on atteint dimanche (fin de semaine), nouvelle semaine
            if weekday == 6 or day == days_in_month:
                # Compléter la semaine si nécessaire
                while len(week) < 7:
                    week.append(None)
                weeks.append(week)
                week = []
        
        months_data.append({
            'name': months_names[month - 1],
            'year': actual_year,
            'weeks': weeks,
            'planned_hours': fmt(month_planned_minutes),
            'overtime_hours': fmt(month_overtime_minutes),
            'delay_hours': fmt(month_delay_minutes),
        })
    
    # Statistiques
    stats = {
        'preventive_tasks': len(scheduled_tasks),
        'curative_interventions': sum(1 for i in interventions if i.type == 'curatif'),
        'admin_tasks': AdminTask.query.filter_by(is_active=True).count(),
        'pending_items': PlanningItem.query.filter_by(status='planned').count(),
    }
    
    return render_template('planning/calendar.html',
        year=year,
        mode=mode,
        months=months_data,
        stats=stats
    )


@planning_bp.route('/day/<date_str>')
@login_required
def day(date_str):
    """Afficher le détail d'une journée."""
    from datetime import datetime
    from ..core.services.planning_service import PlanningService
    
    try:
        d = datetime.strptime(date_str, '%Y-%m-%d').date()
    except ValueError:
        flash('Date invalide', 'error')
        return redirect(url_for('planning.index'))
    
    # Récupérer le planning du jour
    schedule = PlanningService.get_day_schedule(d)
    
    # Récupérer les tâches planifiées pour ce jour
    scheduled_tasks = ScheduledTask.query.filter(
        ScheduledTask.scheduled_date == d,
        ScheduledTask.status.in_(('planned', 'in_progress', 'suspended', 'postponed')),
        ScheduledTask.intervention_id.is_(None),
    ).all()
    
    return render_template('planning/day.html',
        date=d,
        schedule=schedule,
        tasks=scheduled_tasks
    )


@planning_bp.route('/tasks')
@login_required
def tasks():
    """Liste des tâches préventives."""
    from app_new.core.models.maintenance import LotTask
    tasks = LotTask.query.join(Lot).order_by(Lot.name, LotTask.num_tache).all()
    lots = Lot.query.order_by(Lot.name).all()
    categories = EquipmentCategory.query.order_by(EquipmentCategory.name).all()
    
    return render_template('planning/tasks.html', 
                          tasks=tasks, 
                          lots=lots, 
                          categories=categories)


@planning_bp.route('/tasks/<int:id>/edit', methods=['GET', 'POST'])
@login_required
def edit_task(id):
    """Modifier une tâche préventive."""
    from app_new.core.models.maintenance import LotTask
    task = LotTask.query.get_or_404(id)
    
    if request.method == 'POST':
        duree = request.form.get('duree_minutes')
        task.duree_minutes = int(duree) if duree and duree.isdigit() else None
        task.gestionnaire = request.form.get('gestionnaire') or None
        task.periodicite = request.form.get('periodicite') or None
        task.jours_entre_interventions = int(request.form.get('jours_entre_interventions')) if request.form.get('jours_entre_interventions') and request.form.get('jours_entre_interventions').isdigit() else None
        task.type_maintenance = request.form.get('type_maintenance') or None
        task.trigger_type = request.form.get('trigger_type', 'calendar')
        task.trigger_event = request.form.get('trigger_event') or None
        task.meter_threshold = request.form.get('meter_threshold', type=float)
        task.season_start_month = request.form.get('season_start_month', type=int)
        task.season_end_month = request.form.get('season_end_month', type=int)
        task.advance_days = request.form.get('advance_days', type=int, default=0)
        task.is_active = request.form.get('is_active') == 'on'
        task.contrat = request.form.get('contrat') == 'on'
        
        db.session.commit()
        flash('Tâche mise à jour', 'success')
        return redirect(url_for('planning.tasks'))
    
    return render_template('planning/edit_task.html', task=task)


@planning_bp.route('/tasks/<int:id>/generate', methods=['POST'])
@login_required
def generate_task(id):
    from app_new.core.models.maintenance import LotTask
    from app_new.core.services.maintenance_engine import generate_due_tasks
    task = LotTask.query.get_or_404(id)
    created = generate_due_tasks(task, event=(request.form.get('event') or '').strip() or None)
    flash(f'{len(created)} échéance(s) créée(s), sans doublon.', 'success' if created else 'info')
    return redirect(url_for('planning.tasks'))


@planning_bp.route('/tasks/new', methods=['GET', 'POST'])
@login_required
def new_task():
    """Créer une nouvelle tâche préventive."""
    if request.method == 'POST':
        task = LotTask(
            lot_id=request.form.get('lot_id', type=int),
            num_tache=request.form.get('task_number'),
            tache=request.form.get('name'),
            periodicite=request.form.get('periodicite'),
            jours_entre_interventions=request.form.get('period_days', type=int),
            duree_minutes=request.form.get('duration_minutes', type=int, default=30),
            contrat=request.form.get('has_contract') == 'on',
            gestionnaire=request.form.get('manager'),
            trigger_type=request.form.get('trigger_type', 'calendar'),
            trigger_event=request.form.get('trigger_event') or None,
            meter_threshold=request.form.get('meter_threshold', type=float),
            season_start_month=request.form.get('season_start_month', type=int),
            season_end_month=request.form.get('season_end_month', type=int),
            is_active=True
        )
        db.session.add(task)
        db.session.commit()
        flash('Tâche préventive créée', 'success')
        return redirect(url_for('planning.tasks'))
    
    lots = Lot.query.order_by(Lot.name).all()
    categories = EquipmentCategory.query.order_by(EquipmentCategory.name).all()
    companies = Company.query.order_by(Company.name).all()
    
    return render_template('planning/task_form.html',
                          title='Nouvelle tâche préventive',
                          task=None,
                          lots=lots,
                          categories=categories,
                          companies=companies)


@planning_bp.route('/scheduled')
@login_required
def scheduled():
    """Liste des tâches planifiées."""
    status = request.args.get('status', 'all')
    date_filter = request.args.get('date')
    
    query = ScheduledTask.query.filter(ScheduledTask.equipment_id.isnot(None))
    
    if status != 'all':
        query = query.filter_by(status=status)
    
    if date_filter:
        query = query.filter_by(scheduled_date=datetime.strptime(date_filter, '%Y-%m-%d').date())
    
    tasks = query.order_by(ScheduledTask.scheduled_date, ScheduledTask.scheduled_start).all()
    
    return render_template('planning/scheduled.html', tasks=tasks, status=status)


@planning_bp.route('/scheduled/reconcile', methods=['POST'])
@login_required
def reconcile_scheduled():
    from app_new.core.services.maintenance_engine import reconcile_planned_tasks
    changed = reconcile_planned_tasks()
    flash(f'{len(changed)} échéance(s) déplacée(s) vers un jour travaillé.', 'success' if changed else 'info')
    return redirect(url_for('planning.scheduled'))


@planning_bp.route('/scheduled/<int:id>')
@login_required
def scheduled_detail(id):
    """Détail d'une tâche planifiée."""
    task = ScheduledTask.query.get_or_404(id)
    return render_template('planning/scheduled_detail.html', task=task)


@planning_bp.route('/meters')
@login_required
def meters():
    """Liste des compteurs."""
    meters = Meter.query.order_by(Meter.name).all()
    return render_template('planning/meters.html', meters=meters)


@planning_bp.route('/meters/new', methods=['GET', 'POST'])
@login_required
def new_meter():
    """Créer un nouveau compteur."""
    if request.method == 'POST':
        from ..core.services.meter_service import create_meter, MeterDomainError
        try:
            meter = create_meter(
                equipment_id=request.form.get('equipment_id', type=int),
                name=request.form.get('name'),
                meter_type=request.form.get('meter_type', 'hours'),
                unit=request.form.get('unit', 'h'),
                current_value=request.form.get('current_value', type=float, default=0),
                initial_value=request.form.get('initial_value', type=float, default=0),
                warning_threshold=request.form.get('warning_threshold', type=float),
                critical_threshold=request.form.get('critical_threshold', type=float),
                maintenance_interval=request.form.get('maintenance_interval', type=float),
            )
        except MeterDomainError as exc:
            flash(str(exc), 'danger')
            return redirect(url_for('planning.new_meter'))
        flash('Compteur créé', 'success')
        return redirect(url_for('planning.meters'))
    
    from ..core.models.equipment import Equipment
    equipments = Equipment.query.filter_by(is_group=False).order_by(Equipment.name).all()
    
    return render_template('planning/meter_form.html',
                          title='Nouveau compteur',
                          meter=None,
                          equipments=equipments)


@planning_bp.route('/consumables')
@login_required
def consumables():
    """Liste des consommables."""
    consumables = Consumable.query.order_by(Consumable.category, Consumable.name).all()
    return render_template('planning/consumables.html', consumables=consumables)


@planning_bp.route('/consumables/new', methods=['GET', 'POST'])
@login_required
def new_consumable():
    """Créer un nouveau consommable."""
    if request.method == 'POST':
        consumable = Consumable(
            name=request.form.get('name'),
            reference=request.form.get('reference'),
            category=request.form.get('category'),
            quantity=request.form.get('quantity', type=int, default=0),
            min_quantity=request.form.get('min_quantity', type=int, default=0),
            unit=request.form.get('unit', 'pièce'),
            unit_price=request.form.get('unit_price', type=float),
            supplier_id=request.form.get('supplier_id', type=int) or None,
            storage_location=request.form.get('storage_location'),
            is_active=True
        )
        db.session.add(consumable)
        db.session.commit()
        flash('Consommable créé', 'success')
        return redirect(url_for('planning.consumables'))
    
    companies = Company.query.order_by(Company.name).all()
    
    return render_template('planning/consumable_form.html',
                          title='Nouveau consommable',
                          consumable=None,
                          companies=companies)


@planning_bp.route('/consumables/<int:id>/edit', methods=['GET', 'POST'])
@login_required
def edit_consumable(id):
    consumable = Consumable.query.get_or_404(id)
    if request.method == 'POST':
        consumable.name = (request.form.get('name') or '').strip()
        consumable.reference = request.form.get('reference') or None
        consumable.category = request.form.get('category') or None
        consumable.quantity = max(0, request.form.get('quantity', type=int, default=0))
        consumable.min_quantity = max(0, request.form.get('min_quantity', type=int, default=0))
        consumable.unit = request.form.get('unit') or 'pièce'
        consumable.unit_price = request.form.get('unit_price', type=float)
        consumable.supplier_id = request.form.get('supplier_id', type=int) or None
        consumable.storage_location = request.form.get('storage_location') or request.form.get('location') or None
        db.session.commit()
        flash('Consommable mis à jour', 'success')
        return redirect(url_for('planning.consumables'))
    companies = Company.query.order_by(Company.name).all()
    return render_template('planning/consumable_form.html', title='Modifier le consommable',
                           consumable=consumable, companies=companies)
