# Fichier: blueprints/manager.py (VERSION TEAM MEMBERS READY)

from flask import Blueprint, render_template, request, redirect, url_for, flash, current_app, abort
from flask_login import login_required, current_user
import secrets
import string
import re
from flask_mail import Message 
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import joinedload
from sqlalchemy import func
from datetime import datetime, timezone, timedelta

# Importations
from models import User, Rapport
from extensions import db, bcrypt, mail, limiter
from decorators import role_required 
from models import User, Rapport, AuditLog # <--- Ajoute AuditLog ici

manager = Blueprint('manager', __name__, url_prefix='/manager', template_folder='../templates')

# ========================================================================
# FONCTIONS UTILITAIRES
# ========================================================================

def generate_random_password(length=32):
    chars = string.ascii_letters + string.digits + "!@#$%^&*"
    password = ''.join(secrets.choice(chars) for _ in range(length))
    if not (any(c.isupper() for c in password) and 
            any(c.islower() for c in password) and 
            any(c.isdigit() for c in password) and 
            any(c in "!@#$%^&*" for c in password)):
        return generate_random_password(length)
    return password

def validate_email_format(email):
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return re.match(pattern, email) is not None

def send_new_client_email(user):
    """Envoie l'email de bienvenue au Client Final"""
    try:
        token = user.get_reset_token(expires_sec=86400, secret_key=str(current_app.config['SECRET_KEY']))
        msg = Message('Bienvenue sur MCyber Audit - Activation de votre compte', recipients=[user.email])
        reset_url = url_for('auth.reset_password', token=token, _external=True)
        msg.body = f"""Bonjour,

Votre partenaire de sécurité vous invite sur le Portail MCyber Audit.

📋 VOS ACCÈS :
- Entreprise : {user.company_name or 'Non renseignée'}
- Identifiant : {user.email}

🔐 Pour définir votre mot de passe et accéder à la plateforme, cliquez ici :
{reset_url}

Ce lien est sécurisé et valide 24 heures.

Cordialement,
L'équipe MCyber Consulting
"""
        mail.send(msg)
        return True
    except Exception as e:
        current_app.logger.error(f"Erreur envoi email invitation à {user.email}: {e}", exc_info=True)
        return False

# ✅ NOUVEAU : Email pour les membres d'équipe
def send_team_invitation_email(user, temp_password):
    """Envoie l'email d'invitation à un membre de l'équipe (Manager secondaire)"""
    try:
        token = user.get_reset_token(expires_sec=86400, secret_key=str(current_app.config['SECRET_KEY']))
        msg = Message('Invitation équipe - MCyber Audit', recipients=[user.email])
        reset_url = url_for('auth.reset_password', token=token, _external=True)
        
        msg.body = f"""Bonjour,

L'administrateur de votre compte MCyber Audit vous a ajouté à l'équipe.

📋 VOS ACCÈS MANAGER :
- Identifiant : {user.email}
- Rôle : Membre d'équipe

🔐 Cliquez ici pour définir votre mot de passe personnel :
{reset_url}

Ce lien est valide 24 heures.

Cordialement,
L'équipe MCyber Consulting
"""
        mail.send(msg)
        return True
    except Exception as e:
        current_app.logger.error(f"Erreur envoi email team à {user.email}: {e}", exc_info=True)
        return False

def get_msp_fleet_data(manager_id):
    """Retourne les données consolidées pour la Console MSP."""
    # 1. Clients
    clients = User.query.filter_by(manager_id=manager_id).options(joinedload(User.rapports)).all()
    client_ids = [c.id for c in clients]
    clients_data_list = []
    
    # 2. Calculs par client
    for client in clients:
        sorted_reports = sorted(client.rapports, key=lambda r: r.date_creation, reverse=True)
        last_report = sorted_reports[0] if sorted_reports else None
        days_since = None
        if last_report:
            try:
                now_utc = datetime.now(timezone.utc)
                report_date = last_report.date_creation
                if report_date.tzinfo is None:
                    report_date = report_date.replace(tzinfo=timezone.utc)
                days_since = (now_utc - report_date).days
            except Exception as e:
                current_app.logger.warning(f"Date warning for report {last_report.id}: {e}")
        
        clients_data_list.append({'user': client, 'nb_rapports': len(client.rapports), 'last_report_date': last_report.date_creation if last_report else None, 'days_since_audit': days_since})

    # 3. KPI : Audits mois
    thirty_days_ago = datetime.now(timezone.utc) - timedelta(days=30)
    audits_this_month = 0
    if client_ids:
        audits_this_month = Rapport.query.filter(Rapport.user_id.in_(client_ids), Rapport.date_creation >= thirty_days_ago).count()

    # 4. KPI : Champion
    top_user_name = "Aucun"
    top_user_count = 0
    if client_ids:
        champion_data = db.session.query(Rapport.user_id, func.count(Rapport.id).label('total')).filter(Rapport.user_id.in_(client_ids)).group_by(Rapport.user_id).order_by(func.count(Rapport.id).desc()).first()
        if champion_data:
            user_id, total = champion_data
            champion_user = next((c for c in clients if c.id == user_id), None)
            if champion_user:
                top_user_name = champion_user.company_name or champion_user.email
                top_user_count = total

    # 5. KPI : Total Audits
    total_audits = sum(len(c.rapports) for c in clients)
    
    # 6. KPI : Expirés
    now = datetime.now()
    clients_expired = sum(1 for c in clients if c.engagement_end_date and c.engagement_end_date.replace(tzinfo=None) < now)

    return {'clients_data': clients_data_list, 'stats': {'total_clients': len(clients), 'total_audits_clients': total_audits, 'audits_this_month': audits_this_month, 'top_user_name': top_user_name, 'top_user_count': top_user_count, 'clients_expired': clients_expired}}

# ========================================================================
# ROUTES PRINCIPALES
# ========================================================================

@manager.route('/dashboard')
@login_required
@role_required(['manager', 'admin'])
def manager_dashboard():
    try:
        # Configuration
        page = request.args.get('page', 1, type=int)
        sort_by = request.args.get('sort', 'date')
        search = request.args.get('search', '').strip() # 👈 NOUVEAU
        per_page = 10 # 👈 C'est ici pour changer le nombre par page
        
        target_id = current_user.owner_id
        
        # 1. Requête de base
        query = User.query.filter_by(manager_id=target_id)
        
        # 🔍 2. FILTRE RECHERCHE (Nouveau)
        if search:
            query = query.filter(
                (User.company_name.ilike(f'%{search}%')) | 
                (User.email.ilike(f'%{search}%'))
            )
        
        # 3. Tri
        if sort_by == 'name':
            query = query.order_by(User.company_name.asc())
        elif sort_by == 'reports':
            query = query.outerjoin(Rapport).group_by(User.id).order_by(func.count(Rapport.id).desc())
        else: # date
            query = query.order_by(User.created_at.desc())
            
        # 4. Pagination
        pagination = query.paginate(page=page, per_page=per_page, error_out=False)
        
        # 5. Construction des données (Identique avant...)
        clients_data = []
        for client in pagination.items:
            last_report = Rapport.query.filter_by(user_id=client.id).order_by(Rapport.date_creation.desc()).first()
            report_count = Rapport.query.filter_by(user_id=client.id).count()
            
            days_since = None
            if last_report:
                now_utc = datetime.now(timezone.utc)
                if last_report.date_creation.tzinfo is None:
                    report_date = last_report.date_creation.replace(tzinfo=timezone.utc)
                else:
                    report_date = last_report.date_creation
                days_since = (now_utc - report_date).days
            
            clients_data.append({
                'user': client,
                'nb_rapports': report_count,
                'last_report_date': last_report.date_creation if last_report else None,
                'days_since_audit': days_since
            })

        # Stats globales (inchangé)
        total_clients = User.query.filter_by(manager_id=target_id).count()
        all_client_ids = [c.id for c in User.query.filter_by(manager_id=target_id).with_entities(User.id).all()]
        total_audits = 0
        audits_this_month = 0
        if all_client_ids:
            total_audits = Rapport.query.filter(Rapport.user_id.in_(all_client_ids)).count()
            thirty_days_ago = datetime.now(timezone.utc) - timedelta(days=30)
            audits_this_month = Rapport.query.filter(Rapport.user_id.in_(all_client_ids), Rapport.date_creation >= thirty_days_ago).count()

        stats = {
            'total_clients': total_clients,
            'total_audits_clients': total_audits,
            'audits_this_month': audits_this_month,
            'top_user_name': None,
            'top_user_count': 0
        }

        return render_template('manager_dashboard.html', 
                             stats=stats, 
                             clients_data=clients_data,
                             pagination=pagination,
                             current_sort=sort_by,
                             current_search=search, # 👈 On passe la recherche au template
                             now=datetime.now())
                             
    except Exception as e:
        current_app.logger.error(f"Erreur Dashboard MSP: {e}", exc_info=True)
        flash("❌ Erreur lors du chargement de la console.", 'danger')
        return redirect(url_for('main.dashboard'))

@manager.route('/new_client', methods=['GET', 'POST'])
@login_required
@role_required(['manager', 'admin'])
@limiter.limit("100 per hour")
def manager_new_client():
    if request.method == 'POST':
        email = request.form.get('email', '').strip().lower()
        nom_client = request.form.get('nom_client', '').strip()
        
        if not email or not nom_client:
            flash("❌ Tous les champs sont requis.", 'danger')
            return render_template('manager_new_client.html')
        if not validate_email_format(email):
            flash("❌ Format d'email invalide.", 'danger')
            return render_template('manager_new_client.html')
        if len(nom_client) > 150:
            flash("❌ Nom client trop long.", 'danger')
            return render_template('manager_new_client.html')
        if User.query.filter_by(email=email).first():
            flash(f"❌ L'email {email} est déjà utilisé.", 'warning')
            return render_template('manager_new_client.html')
        
        temp_password = generate_random_password(32)
        hashed_password = bcrypt.generate_password_hash(temp_password).decode('utf-8')
        
        # ✅ CHANGÉ : Le manager_id est celui du OWNER (Patron), pas forcément celui qui clique
        new_user = User(
            email=email, 
            password=hashed_password, 
            role='user', 
            company_name=nom_client, 
            manager_id=current_user.owner_id,  # 👈 ICI
            can_audit=True, 
            is_active_account=True
        )
        
        try:
            db.session.add(new_user)
            db.session.commit()
            current_app.logger.info(f"➕ Manager {current_user.email} crée client {email} ({nom_client}) | IP: {request.remote_addr}")
            
            if send_new_client_email(new_user): 
                flash(f'✅ Client {nom_client} créé et invité par email.', 'success')
            else: 
                flash(f'⚠️ Client créé, mais erreur email.', 'warning')
            return redirect(url_for('manager.manager_dashboard'))
        except IntegrityError:
            db.session.rollback()
            flash(f"❌ L'email {email} est déjà utilisé.", 'danger')
        except Exception as e:
            db.session.rollback()
            current_app.logger.error(f"Erreur création client: {e}", exc_info=True)
            flash("❌ Erreur technique.", 'danger')
            
    return render_template('manager_new_client.html')

# MODIFICATION UUID : <string:client_id>
@manager.route('/client/<string:client_id>/rapports')
@login_required
@role_required(['manager', 'admin'])
def client_rapports(client_id):
    client = User.query.get_or_404(client_id)
    
    # ✅ SÉCURITÉ : On vérifie si le client appartient au OWNER
    if current_user.role == 'manager' and client.manager_id != current_user.owner_id:
        current_app.logger.warning(f"⚠️ Tentative accès non autorisé : Manager {current_user.email} → Client {client.email}")
        flash("❌ Accès non autorisé.", 'danger')
        return redirect(url_for('manager.manager_dashboard'))
    
    # ✅ PAGINATION & RECHERCHE SERVEUR
    page = request.args.get('page', 1, type=int)
    search = request.args.get('search', '').strip()
    per_page = 10 # Nombre de rapports par page
    
    # Query de base
    query = Rapport.query.filter_by(user_id=client_id)
    
    # Filtre Recherche (Nom entité ou Score)
    if search:
        query = query.filter(Rapport.nom_client.ilike(f'%{search}%'))
        
    # Tri et Pagination
    pagination = query.order_by(Rapport.date_creation.desc())\
                      .paginate(page=page, per_page=per_page, error_out=False)
    
    return render_template('manager_client_rapports.html', 
                           client=client, 
                           rapports=pagination.items, 
                           pagination=pagination,
                           current_search=search,
                           now=datetime.now())

# ========================================================================
# ACTIONS SUR CLIENT (SÉCURITÉ OWNER_ID)
# ========================================================================

@manager.route('/client/<string:client_id>/toggle_access', methods=['POST'])
@login_required
@role_required(['manager', 'admin'])
@limiter.limit("100 per hour")
def toggle_client_access(client_id):
    client = User.query.get_or_404(client_id)
    # ✅ Check Owner
    if current_user.role == 'manager' and client.manager_id != current_user.owner_id:
        flash("❌ Action non autorisée.", 'danger')
        return redirect(url_for('manager.manager_dashboard'))
    
    client.is_active_account = not client.is_active_account
    action = "réactivé" if client.is_active_account else "suspendu"
    db.session.commit()
    current_app.logger.warning(f"🔒 Manager {current_user.email} a {action} le compte {client.email}")
    flash(f"✅ Le compte de {client.company_name} a été {action}.", 'success')
    return redirect(url_for('manager.client_rapports', client_id=client.id))

@manager.route('/client/<string:client_id>/toggle_audit', methods=['POST'])
@login_required
@role_required(['manager', 'admin'])
@limiter.limit("100 per hour")
def toggle_audit_permission(client_id):
    client = User.query.get_or_404(client_id)
    # ✅ Check Owner
    if current_user.role == 'manager' and client.manager_id != current_user.owner_id:
        flash("❌ Action non autorisée.", 'danger')
        return redirect(url_for('manager.manager_dashboard'))
        
    client.can_audit = not client.can_audit
    action = "autorisée" if client.can_audit else "bloquée"
    db.session.commit()
    current_app.logger.info(f"📝 Manager {current_user.email} a {action} la création d'audit pour {client.email}")
    flash(f"✅ La création d'audit est {action}.", "success")
    return redirect(url_for('manager.client_rapports', client_id=client.id))

@manager.route('/client/<string:client_id>/delete', methods=['POST'])
@login_required
@role_required(['manager', 'admin'])
@limiter.limit("100 per hour")
def delete_client(client_id):
    client = User.query.get_or_404(client_id)
    # ✅ Check Owner
    if current_user.role == 'manager' and client.manager_id != current_user.owner_id:
        flash("❌ Action non autorisée.", 'danger')
        return redirect(url_for('manager.manager_dashboard'))
    
    client_email = client.email
    current_app.logger.warning(f"🗑️ Manager {current_user.email} supprime client {client_email}")
    
    try:
        db.session.delete(client)
        db.session.commit()
        flash(f"✅ Client {client_email} supprimé définitivement.", 'success')
        return redirect(url_for('manager.manager_dashboard'))
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Erreur suppression client {client_id}: {e}", exc_info=True)
        flash("❌ Erreur technique.", 'danger')
        return redirect(url_for('manager.client_rapports', client_id=client.id))

@manager.route('/client/<string:client_id>/update_engagement', methods=['POST'])
@login_required
@role_required(['manager', 'admin'])
def update_client_engagement(client_id):
    client = User.query.get_or_404(client_id)
    # ✅ Check Owner
    if current_user.role == 'manager' and client.manager_id != current_user.owner_id:
        flash("❌ Action non autorisée.", 'danger')
        return redirect(url_for('manager.manager_dashboard'))
    
    date_str = request.form.get('engagement_date', '').strip()
    if date_str:
        try:
            client.engagement_end_date = datetime.strptime(date_str, '%Y-%m-%d')
            db.session.commit()
            current_app.logger.info(f"📅 Manager {current_user.email} modifie engagement pour {client.email}: {date_str}")
            flash("✅ Date d'engagement mise à jour.", 'success')
        except ValueError:
            flash("❌ Format de date invalide.", 'warning')
    else:
        client.engagement_end_date = None
        db.session.commit()
        flash("✅ Date d'engagement supprimée.", 'info')
            
    return redirect(url_for('manager.client_rapports', client_id=client.id))

# ========================================================================
# ✅ GESTION D'ÉQUIPE (NOUVEAU)
# ========================================================================

@manager.route('/team')
@login_required
@role_required('manager')
def manage_team():
    """Affiche la liste des membres de l'équipe"""
    
    # 🚨 SÉCURITÉ : Seul le patron (celui qui n'a pas de parent) gère l'équipe
    if current_user.parent_id:
        flash("❌ Seul le titulaire principal du compte peut gérer l'équipe.", "warning")
        return redirect(url_for('manager.manager_dashboard'))

    # On récupère les membres rattachés à ce manager
    members = User.query.filter_by(parent_id=current_user.id).all()
    
    return render_template('manager_team.html', members=members)

@manager.route('/team/add', methods=['POST'])
@login_required
@role_required('manager')
def add_team_member():
    """Ajouter un collaborateur"""
    
    # 🚨 SÉCURITÉ
    if current_user.parent_id:
        flash("Action non autorisée.", 'danger')
        return redirect(url_for('manager.manager_dashboard'))
        
    email = request.form.get('email', '').strip().lower()
    nom = request.form.get('name', '').strip() # Nom de l'employé (optionnel, on peut utiliser company_name)
    
    if not email:
        flash("L'email est requis.", 'danger')
        return redirect(url_for('manager.manage_team'))
        
    if User.query.filter_by(email=email).first():
        flash("Cet email est déjà utilisé.", 'danger')
        return redirect(url_for('manager.manage_team'))

    # Génération password temp
    temp_password = generate_random_password(24)
    hashed_pw = bcrypt.generate_password_hash(temp_password).decode('utf-8')
    
    # Création du membre
    new_member = User(
        email=email,
        password=hashed_pw,
        role='manager',         # Rôle manager pour avoir accès à la console
        parent_id=current_user.id, # 👈 LIEN PARENT
        company_name=nom or email.split('@')[0],
        is_active_account=True,
        max_concurrent_sessions=1 # Par défaut
    )
    
    try:
        db.session.add(new_member)
        db.session.commit()
        
        # Envoi invitation
        if send_team_invitation_email(new_member, temp_password):
            flash(f"✅ Invitation envoyée à {email}.", "success")
        else:
            flash(f"⚠️ Utilisateur créé mais erreur d'envoi d'email.", "warning")
            
        current_app.logger.info(f"👥 Manager {current_user.email} ajoute membre {email}")
        
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Erreur ajout team member: {e}", exc_info=True)
        flash("Erreur technique lors de l'ajout.", "danger")
    
    return redirect(url_for('manager.manage_team'))

@manager.route('/team/delete/<string:member_id>', methods=['POST'])
@login_required
@role_required('manager')
def delete_team_member(member_id):
    """Supprimer un collaborateur"""
    
    # 🚨 SÉCURITÉ
    if current_user.parent_id:
        abort(403)
        
    member = User.query.get_or_404(member_id)
    
    # Vérifier que ce membre appartient bien à ce manager
    if member.parent_id != current_user.id:
        flash("Action non autorisée.", "danger")
        return redirect(url_for('manager.manage_team'))
        
    try:
        email = member.email
        db.session.delete(member)
        db.session.commit()
        flash(f"✅ Membre {email} supprimé.", "success")
        current_app.logger.info(f"👥 Manager {current_user.email} supprime membre {email}")
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f"Erreur suppression team member: {e}")
        flash("Erreur lors de la suppression.", "danger")
        
    return redirect(url_for('manager.manage_team'))

    # ========================================================================
# LOGS D'ACTIVITÉ (Scope Manager)
# ========================================================================

@manager.route('/logs')
@login_required
@role_required(['manager', 'admin'])
def activity_logs():
    """Affiche les logs d'activité de toute l'organisation du manager (Équipe + Clients)"""
    
    # 1. Définir le périmètre (Scope)
    owner_id = current_user.owner_id
    
    # Récupérer tous les ID liés à cette organisation :
    # - Le Patron (Owner)
    # - Les Membres d'équipe (parent_id = owner_id)
    # - Les Clients (manager_id = owner_id)
    
    team_ids = [u.id for u in User.query.filter((User.id == owner_id) | (User.parent_id == owner_id)).all()]
    client_ids = [u.id for u in User.query.filter_by(manager_id=owner_id).all()]
    
    # Liste complète des IDs autorisés
    allowed_user_ids = team_ids + client_ids
    
    # 2. Filtres
    page = request.args.get('page', 1, type=int)
    period = request.args.get('period', '7d')
    category = request.args.get('category', 'all')
    user_search = request.args.get('user', '').strip()
    
    # 3. Requête de base (Filtrée par les IDs autorisés)
    query = AuditLog.query.filter(
        AuditLog.user_id.in_(allowed_user_ids),
        AuditLog.level.in_(['INFO', 'WARNING']) # On cache les erreurs techniques aux managers
    )
    
    # Application des filtres
    if period != 'all':
        days = int(period.rstrip('d'))
        cutoff = datetime.now(timezone.utc) - timedelta(days=days)
        query = query.filter(AuditLog.timestamp >= cutoff)
        
    if category != 'all':
        query = query.filter(AuditLog.category == category.upper())
        
    if user_search:
        # Jointure pour filtrer par email
        query = query.join(User).filter(User.email.ilike(f'%{user_search}%'))
        
    # 4. Pagination
    pagination = query.order_by(AuditLog.timestamp.desc())\
                      .paginate(page=page, per_page=20, error_out=False)
    
    return render_template(
        'manager_logs.html',
        logs=pagination.items,
        pagination=pagination,
        period=period,
        category=category,
        user_search=user_search
    )