"""Outlook Sync Routes - GMAO Collège"""
from flask import Blueprint, current_app, render_template, redirect, url_for, request, flash, jsonify, send_file
from flask_login import login_required, current_user
from app_new.extensions import db
from ..outlook.models import OutlookAccount, OutlookMail, OutlookFolder, OutlookAttachment, OutlookMailInterpretation, GmaoContext
from ..outlook.auth import get_access_token
from ..core.models.maintenance import WatchdogLog
import os
import subprocess
import json
import tempfile

sync_bp = Blueprint('outlook_sync', __name__, template_folder='templates')


def _sync_error_message(exc, *, account_id, folder_id=None, mail_data=None):
    """Produit un journal utile sans inclure le corps ni les destinataires du mail."""
    original = getattr(exc, 'orig', None)
    error_code = getattr(original, 'args', [None])[0] if original else None
    subject = ((mail_data or {}).get('Subject') or '(sans objet)')[:160]
    mail_id = ((mail_data or {}).get('Id') or '(inconnu)')[:180]
    content = ((mail_data or {}).get('Body') or {}).get('Content') or ''
    detail = str(original or exc).split('[SQL:', 1)[0].strip()[:500]
    return (
        f"Synchronisation Outlook impossible pour le message {mail_id} "
        f"(compte={account_id}, dossier={folder_id or 'inbox'}, objet={subject!r}, "
        f"corps={len(content.encode('utf-8'))} octets, code={error_code}): {detail}"
    )


def _store_sync_error(message):
    """Ajoute une erreur à la page /logs/ dans la transaction courante."""
    db.session.add(WatchdogLog(
        watchdog_name='outlook_sync', level='error', message=message[:10000],
    ))

@sync_bp.route('/api/sync-folders/<int:account_id>', methods=['POST'])
@login_required
def api_sync_folders(account_id):
    """Synchronise les dossiers Outlook (avec sous-dossiers récursifs)."""
    import requests
    from datetime import datetime, timezone
    
    account = OutlookAccount.query.get(account_id)
    if not account or (account.user_id != current_user.id and not current_user.is_admin()):
        return jsonify({'error': 'Compte non trouvé'}), 404
    
    access_token, error = get_access_token(account)
    if error:
        return jsonify({'error': error}), 401
    
    try:
        headers = {'Authorization': f'Bearer {access_token}'}
        
        # Fonction récursive pour récupérer les dossiers
        def fetch_folders(parent_id=None, depth=0):
            if depth > 5:  # Limiter la profondeur
                return []
            
            folders = []
            if parent_id:
                url = f'https://outlook.office365.com/api/v2.0/me/MailFolders/{parent_id}/ChildFolders'
            else:
                url = 'https://outlook.office365.com/api/v2.0/me/MailFolders'
            
            while url:
                resp = requests.get(url, headers=headers)
                if resp.status_code != 200:
                    break
                
                data = resp.json()
                for folder_data in data.get('value', []):
                    folders.append(folder_data)
                    # Récupérer les sous-dossiers récursivement
                    child_folders = fetch_folders(folder_data.get('Id'), depth + 1)
                    folders.extend(child_folders)
                
                url = data.get('@odata.nextLink')
            
            return folders
        
        all_folders = fetch_folders()
        synced = 0
        
        for folder_data in all_folders:
            folder_id = folder_data.get('Id')
            existing = OutlookFolder.query.filter_by(id=folder_id).first()
            
            if not existing:
                folder_name = folder_data.get('DisplayName') or folder_data.get('Name') or 'Inconnu'
                folder_type = folder_data.get('WellKnownFolderName', 'custom')
                
                default_folders = {
                    'inbox': 'Boîte de réception',
                    'sentitems': 'Éléments envoyés',
                    'drafts': 'Brouillons',
                    'deleteditems': 'Éléments supprimés',
                    'junkemail': 'Courrier indésirable',
                    'outbox': "Boîte d'envoi",
                    'archive': 'Archive'
                }
                
                display_name = default_folders.get(folder_type, folder_name)
                
                folder = OutlookFolder(
                    id=folder_id,
                    account_id=account.id,
                    name=folder_name,
                    display_name=display_name,
                    parent_id=folder_data.get('ParentFolderId'),
                    total_items=folder_data.get('TotalItemCount', 0),
                    unread_items=folder_data.get('UnreadItemCount', 0),
                    folder_type=folder_type
                )
                db.session.add(folder)
                synced += 1
            else:
                existing.total_items = folder_data.get('TotalItemCount', 0)
                existing.unread_items = folder_data.get('UnreadItemCount', 0)
                folder_name = folder_data.get('DisplayName') or folder_data.get('Name') or existing.name
                existing.name = folder_name
                folder_type = folder_data.get('WellKnownFolderName', 'custom')
                default_folders = {
                    'inbox': 'Boîte de réception',
                    'sentitems': 'Éléments envoyés',
                    'drafts': 'Brouillons',
                    'deleteditems': 'Éléments supprimés',
                    'junkemail': 'Courrier indésirable',
                    'outbox': "Boîte d'envoi",
                    'archive': 'Archive'
                }
                existing.display_name = default_folders.get(folder_type, folder_name)
        
        db.session.commit()
        
        return jsonify({
            'success': True,
            'message': f'{synced} nouveaux dossiers synchronisés',
            'total': len(all_folders)
        })
        
    except Exception as e:
        import traceback
        traceback.print_exc()
        return jsonify({'error': f'Erreur: {str(e)}'}), 500


@sync_bp.route('/api/sync-mails/<int:account_id>/<folder_id>', methods=['POST'])
@sync_bp.route('/api/sync-mails/<int:account_id>', defaults={'folder_id': 'inbox'}, methods=['POST'])
@login_required
def api_sync_mails(account_id, folder_id='inbox'):
    """Synchronise les mails d'un dossier avec pagination complète."""
    import requests
    from datetime import datetime, timezone
    
    account = OutlookAccount.query.get(account_id)
    if not account or (account.user_id != current_user.id and not current_user.is_admin()):
        return jsonify({'error': 'Compte non trouvé'}), 404
    
    access_token, error = get_access_token(account)
    if error:
        return jsonify({'error': error}), 401
    
    # Paramètres de pagination
    max_messages = max(1, min(request.args.get('max', 200, type=int) or 200, 5000))
    
    try:
        # Trouver le dossier
        folder = None
        if folder_id != 'inbox':
            folder = OutlookFolder.query.filter_by(id=folder_id, account_id=account_id).first()
        else:
            # Chercher par folder_type ou par nom (Boîte de réception)
            folder = OutlookFolder.query.filter_by(account_id=account_id, folder_type='inbox').first()
            if not folder:
                folder = OutlookFolder.query.filter(
                    OutlookFolder.account_id == account_id,
                    OutlookFolder.name.like('%ception%')
                ).first()
        
        if not folder:
            return jsonify({'error': 'Dossier non trouvé'}), 404
        
        headers = {'Authorization': f'Bearer {access_token}'}
        
        # Récupérer tous les messages avec pagination
        all_mails = []
        url = f'https://outlook.office365.com/api/v2.0/me/MailFolders/{folder.id}/messages?$top=100&$orderby=ReceivedDateTime desc&$select=Id,Subject,From,ReceivedDateTime,IsRead,HasAttachments,BodyPreview,Body,ToRecipients,CcRecipients,Importance'
        
        while url and len(all_mails) < max_messages:
            resp = requests.get(url, headers=headers)
            
            if resp.status_code != 200:
                error_data = resp.json()
                error_msg = error_data.get('error', {}).get('message', resp.text) if isinstance(error_data, dict) else resp.text
                return jsonify({'error': f'Erreur API Outlook: {error_msg}'}), 500
            
            data = resp.json()
            mails_data = data.get('value', [])
            all_mails.extend(mails_data)
            
            # Vérifier s'il y a une page suivante
            url = data.get('@odata.nextLink')
            
            # Arrêter si on a atteint la limite
            if len(all_mails) >= max_messages:
                break
        
        saved_count = 0
        updated_count = 0
        skipped_count = 0
        
        for mail_data in all_mails:
            try:
                # Le SAVEPOINT et le flush immédiat empêchent un message
                # invalide de faire échouer les suivants par autoflush.
                with db.session.begin_nested():
                    mail_id = mail_data.get('Id')
                    existing = OutlookMail.query.filter_by(id=mail_id).first()
                    from_addr = mail_data.get('From', {}).get('EmailAddress', {})
                    body = mail_data.get('Body', {})
                    received_str = mail_data.get('ReceivedDateTime', '')
                    try:
                        received_at = datetime.fromisoformat(received_str.replace('Z', '+00:00'))
                    except (TypeError, ValueError):
                        received_at = datetime.now(timezone.utc)

                    to_recipients = json.dumps([r.get('EmailAddress', {}).get('Address', '') for r in mail_data.get('ToRecipients', [])])
                    cc_recipients = json.dumps([r.get('EmailAddress', {}).get('Address', '') for r in mail_data.get('CcRecipients', [])])

                    if not existing:
                        db.session.add(OutlookMail(
                            id=mail_id, account_id=account.id, folder_id=folder.id,
                            subject=mail_data.get('Subject', ''),
                            from_name=from_addr.get('Name', ''),
                            from_email=from_addr.get('Address', ''),
                            body_preview=mail_data.get('BodyPreview', ''),
                            body_content=body.get('Content', ''),
                            body_type=body.get('ContentType', 'text'),
                            received_at=received_at,
                            is_read=mail_data.get('IsRead', False),
                            importance=mail_data.get('Importance', 'normal'),
                            has_attachments=mail_data.get('HasAttachments', False),
                            to_recipients=to_recipients,
                            cc_recipients=cc_recipients,
                        ))
                        db.session.flush()
                        saved_count += 1
                    elif existing.is_read != mail_data.get('IsRead', False):
                        existing.is_read = mail_data.get('IsRead', False)
                        db.session.flush()
                        updated_count += 1
            except Exception as mail_error:
                skipped_count += 1
                message = _sync_error_message(
                    mail_error, account_id=account.id, folder_id=folder.id,
                    mail_data=mail_data,
                )
                current_app.logger.warning(message, exc_info=True)
                _store_sync_error(message)
        
        # Mettre à jour les infos du dossier
        folder.total_items = len(all_mails)
        folder.unread_items = sum(1 for m in all_mails if not m.get('IsRead', False))
        
        account.last_connected_at = datetime.now(timezone.utc)
        db.session.commit()
        
        return jsonify({
            'success': True,
            'message': f'{saved_count} nouveaux mails, {updated_count} mis à jour sur {len(all_mails)} récupérés',
            'total': len(all_mails),
            'new': saved_count,
            'updated': updated_count,
            'skipped': skipped_count,
            'warning': f'{skipped_count} message(s) ignoré(s), détails dans /logs/.' if skipped_count else None,
        })
        
    except Exception as e:
        current_app.logger.exception('Échec global de la synchronisation Outlook')
        db.session.rollback()
        message = _sync_error_message(
            e, account_id=account_id, folder_id=folder_id,
        )
        try:
            _store_sync_error(message)
            db.session.commit()
        except Exception:
            db.session.rollback()
            current_app.logger.exception("Impossible d'enregistrer l'erreur Outlook dans /logs/")
        return jsonify({'error': 'La synchronisation Outlook a échoué. Consultez /logs/ pour le détail.'}), 500


# ─── Pièces jointes ─────────────────────────────────────────────────────────

@sync_bp.route('/api/sync-attachments/<mail_id>', methods=['POST'])
@login_required
def api_sync_attachments(mail_id):
    """Synchronise les pièces jointes d'un email."""
    import requests
    
    mail = OutlookMail.query.get_or_404(mail_id)
    account = OutlookAccount.query.get_or_404(mail.account_id)
    if account.user_id != current_user.id and not current_user.is_admin():
        return jsonify({'error': 'Message non trouvé'}), 404
    
    access_token, error = get_access_token(account)
    if error:
        return jsonify({'error': error}), 401
    
    try:
        headers = {'Authorization': f'Bearer {access_token}'}
        resp = requests.get(
            f'https://outlook.office365.com/api/v2.0/me/messages/{mail_id}/attachments',
            headers=headers
        )
        
        if resp.status_code != 200:
            return jsonify({'error': f'Erreur API: {resp.text}'}), 500
        
        attachments_data = resp.json().get('value', [])
        synced = 0
        
        for att_data in attachments_data:
            att_id = att_data.get('Id')
            existing = OutlookAttachment.query.filter_by(id=att_id).first()
            
            if not existing:
                attachment = OutlookAttachment(
                    id=att_id,
                    mail_id=mail_id,
                    name=att_data.get('Name', 'sans_nom'),
                    content_type=att_data.get('ContentType'),
                    size=att_data.get('Size', 0),
                    is_inline=att_data.get('IsInline', False),
                    content_id=att_data.get('ContentId')
                )
                db.session.add(attachment)
                synced += 1
        
        db.session.commit()
        
        return jsonify({
            'success': True,
            'message': f'{synced} nouvelles pièces jointes',
            'total': len(attachments_data)
        })
        
    except Exception as e:
        import traceback
        traceback.print_exc()
        return jsonify({'error': f'Erreur: {str(e)}'}), 500


@sync_bp.route('/attachment/<att_id>/download')
@login_required
def download_attachment(att_id):
    """Télécharge une pièce jointe."""
    import requests
    
    attachment = OutlookAttachment.query.get_or_404(att_id)
    mail = OutlookMail.query.get_or_404(attachment.mail_id)
    account = OutlookAccount.query.get_or_404(mail.account_id)
    if account.user_id != current_user.id and not current_user.is_admin():
        return jsonify({'error': 'Pièce jointe non trouvée'}), 404
    
    access_token, error = get_access_token(account)
    if error:
        flash(error, 'danger')
        return redirect(url_for('outlook_pages.mail_view', account_id=account.id, mail_id=mail.id))
    
    try:
        headers = {'Authorization': f'Bearer {access_token}'}
        resp = requests.get(
            f'https://outlook.office365.com/api/v2.0/me/messages/{mail.id}/attachments/{att_id}/$value',
            headers=headers,
            stream=True
        )
        
        if resp.status_code != 200:
            flash('Erreur lors du téléchargement', 'danger')
            return redirect(url_for('outlook_pages.mail_view', account_id=account.id, mail_id=mail.id))
        
        # Sauvegarder temporairement
        import tempfile
        temp_dir = tempfile.gettempdir()
        temp_path = os.path.join(temp_dir, attachment.name)
        
        with open(temp_path, 'wb') as f:
            for chunk in resp.iter_content(chunk_size=8192):
                f.write(chunk)
        
        return send_file(temp_path, as_attachment=True, download_name=attachment.name)
        
    except Exception as e:
        flash(f'Erreur: {str(e)}', 'danger')
        return redirect(url_for('outlook_pages.mail_view', account_id=account.id, mail_id=mail.id))
