#!/usr/bin/env python3
"""Watchdog pour l'interprétation automatique des emails GMAO - Pattern DND."""

import sys
import os
import time
import json
import signal
import requests
from datetime import datetime, timedelta, timezone

# Définir le répertoire de travail
WORKDIR = os.environ.get('DOCKER_GMAO_WORKDIR', '/root/hermes-workspace/gmao-college')
os.chdir(WORKDIR)
sys.path.insert(0, WORKDIR)

# Activer le venv
venv_site_packages = os.path.join(WORKDIR, 'venv', 'lib', 'python3.13', 'site-packages')
if os.path.exists(venv_site_packages):
    sys.path.insert(0, venv_site_packages)

# Charger les variables d'environnement depuis .env
from dotenv import load_dotenv
env_file = os.environ.get('DOCKER_GMAO_ENVFILE', os.path.join(WORKDIR, '.env'))
load_dotenv(env_file)

# Fichier de log et de dernière exécution
LOG_FILE = '/tmp/gmao_watchdog.log'
LAST_RUN_FILE = '/tmp/gmao_last_run.txt'

running = True

def log(message, level='info'):
    """Écrit un message dans le log et dans la base centralisee."""
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    print(f'[{timestamp}] {message}')
    with open(LOG_FILE, 'a') as f:
        f.write(f'[{timestamp}] {message}\n')
    # Log centralise
    try:
        sys.path.insert(0, '/app')
        from app_new.watchdog_logger import log_watchdog
        log_watchdog('gmao_watchdog', message, level=level)
    except Exception:
        pass


def get_last_run():
    """Retourne la date de dernière exécution."""
    if os.path.exists(LAST_RUN_FILE):
        try:
            with open(LAST_RUN_FILE, 'r') as f:
                return datetime.fromisoformat(f.read().strip())
        except:
            return None
    return None

def save_last_run():
    """Sauvegarde la date de dernière exécution."""
    with open(LAST_RUN_FILE, 'w') as f:
        f.write(datetime.now().isoformat())

def should_run(gmao_context):
    """Vérifie si le watchdog doit s'exécuter selon l'intervalle configuré."""
    if not gmao_context or not gmao_context.auto_interpret_enabled:
        return False
    
    last_run = get_last_run()
    if not last_run:
        return True
    
    interval_minutes = gmao_context.auto_interpret_interval or 10
    next_run = last_run + timedelta(minutes=interval_minutes)
    
    return datetime.now() >= next_run

def sync_inbox():
    """Synchronise la boîte de réception Outlook."""
    from app_new import create_app, db
    from app_new.outlook.models import OutlookAccount, OutlookFolder, OutlookMail, OutlookAttachment
    from app_new.outlook.auth import get_access_token
    
    app = create_app()
    with app.app_context():
        # Récupérer le compte Outlook
        account = OutlookAccount.query.filter_by(is_active=True).first()
        if not account:
            log("Aucun compte Outlook actif")
            return {'success': False, 'error': 'Aucun compte Outlook actif'}
        
        # Obtenir le token
        access_token, error = get_access_token(account)
        if error:
            log(f"Erreur token: {error}")
            return {'success': False, 'error': error}
        
        # Trouver le dossier inbox par nom (Boîte de réception)
        folder = OutlookFolder.query.filter_by(account_id=account.id, name='Boîte de réception').first()
        if not folder:
            # Fallback: chercher par folder_type
            folder = OutlookFolder.query.filter_by(account_id=account.id, folder_type='inbox').first()
        if not folder:
            log("Dossier inbox non trouvé")
            return {'success': False, 'error': 'Dossier inbox non trouvé'}
        
        # Paramètres de synchronisation
        max_messages = 50  # Limite pour le watchdog
        headers = {'Authorization': f'Bearer {access_token}'}
        
        # Récupérer les messages récents
        url = f'https://outlook.office365.com/api/v2.0/me/MailFolders/{folder.id}/messages?$top={max_messages}&$orderby=ReceivedDateTime desc&$select=Id,Subject,From,ReceivedDateTime,IsRead,HasAttachments,BodyPreview,Body,ToRecipients,CcRecipients,Importance'
        
        try:
            resp = requests.get(url, headers=headers, timeout=30)
            
            if resp.status_code != 200:
                error_msg = resp.json().get('error', {}).get('message', resp.text) if resp.json() else resp.text
                log(f"Erreur API Outlook: {error_msg}")
                return {'success': False, 'error': error_msg}
            
            data = resp.json()
            mails_data = data.get('value', [])
            
            saved_count = 0
            updated_count = 0
            
            for mail_data in mails_data:
                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:
                    received_at = datetime.utcnow()
                
                # Destinataires
                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:
                    mail = 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', ''),
                        to_recipients=to_recipients,
                        cc_recipients=cc_recipients,
                        received_at=received_at,
                        is_read=mail_data.get('IsRead', False),
                        has_attachments=mail_data.get('HasAttachments', False),
                        body_preview=mail_data.get('BodyPreview', ''),
                        body_content=body.get('content', '') if body.get('contentType') == 'html' else '',
                        body_type=body.get('contentType', 'text'),
                        importance=mail_data.get('Importance', 'normal')
                    )
                    db.session.add(mail)
                    saved_count += 1
                else:
                    # Mettre à jour si nécessaire
                    existing.is_read = mail_data.get('IsRead', existing.is_read)
                    existing.has_attachments = mail_data.get('HasAttachments', existing.has_attachments)
                    updated_count += 1
            
            db.session.commit()
            
            # Télécharger les pièces jointes PDF pour tous les emails avec pièces jointes
            pdf_count = 0
            emails_with_attachments = [m for m in mails_data if m.get('HasAttachments')]
            
            for mail_data in emails_with_attachments:
                mail_id = mail_data.get('Id')
                try:
                    # Récupérer les pièces jointes
                    att_url = f'https://outlook.office365.com/api/v2.0/me/messages/{mail_id}/attachments'
                    att_resp = requests.get(att_url, headers=headers, timeout=30)
                    
                    if att_resp.status_code == 200:
                        attachments_data = att_resp.json().get('value', [])
                        for att in attachments_data:
                            att_id = att.get('Id')
                            att_name = att.get('Name', '')
                            att_content_type = att.get('ContentType', '')
                            
                            # Vérifier si c'est un PDF
                            if att_name.lower().endswith('.pdf') or 'pdf' in att_content_type.lower():
                                # Vérifier si déjà téléchargé
                                existing_att = OutlookAttachment.query.filter_by(id=att_id).first()
                                if not existing_att:
                                    # Télécharger le contenu
                                    content_url = f'https://outlook.office365.com/api/v2.0/me/messages/{mail_id}/attachments/{att_id}/$value'
                                    content_resp = requests.get(content_url, headers=headers, timeout=60)
                                    
                                    if content_resp.status_code == 200:
                                        new_att = OutlookAttachment(
                                            id=att_id,
                                            mail_id=mail_id,
                                            name=att_name,
                                            content_type=att_content_type,
                                            size=len(content_resp.content),
                                            content=content_resp.content
                                        )
                                        db.session.add(new_att)
                                        pdf_count += 1
                                        log(f"  PDF téléchargé: {att_name}")
                except Exception as e:
                    # Une pièce jointe défectueuse ne doit pas empoisonner la
                    # transaction et faire échouer toute la synchronisation.
                    db.session.rollback()
                    log(f"  Erreur téléchargement pièce jointe {att_name}: {str(e)[:200]}", level='error')
            
            if pdf_count > 0:
                db.session.commit()
                log(f"PDFs téléchargés: {pdf_count}")
            
            # Mettre à jour la date de dernière connexion Outlook
            account.connection_status = 'active'
            account.last_connected_at = datetime.now(timezone.utc)
            db.session.commit()
            
            log(f"Sync: {saved_count} nouveaux, {updated_count} mis à jour")
            return {'success': True, 'saved': saved_count, 'updated': updated_count}
            
        except Exception as e:
            log(f"Erreur sync: {str(e)}")
            return {'success': False, 'error': str(e)}

def run_watchdog():
    """Exécute un cycle du watchdog - retourne False si désactivé."""
    global running
    
    from app_new import create_app, db
    from app_new.outlook.models import GmaoContext
    from app_new.outlook.auto_interpret import run_auto_interpret
    
    app = create_app()
    with app.app_context():
        # Récupérer le contexte GMAO (tous les contextes actifs)
        gmao_context = GmaoContext.query.filter_by(auto_interpret_enabled=True).first()
        
        # Vérifier si activé
        if not gmao_context or not gmao_context.auto_interpret_enabled:
            log("Watchdog désactivé dans la config")
            return False  # Arrêter le watchdog
        
        if not should_run(gmao_context):
            # Pas le moment, attendre
            return True
        
        interval = gmao_context.auto_interpret_interval or 10
        log(f"Exécution de l'interprétation (intervalle: {interval} min)...")
        
        try:
            log("Démarrage synchronisation...")
            # 1. D'abord synchroniser les emails
            sync_result = sync_inbox()
            log(f"Sync result: {sync_result}")
            if sync_result.get('success'):
                log(f"Sync: {sync_result.get('saved', 0)} nouveaux, {sync_result.get('updated', 0)} mis à jour")
            else:
                log(f"Erreur sync inbox: {sync_result.get('error', 'inconnue')}")
            
            log("Démarrage interprétation...")
            # 2. Ensuite interpréter
            result = run_auto_interpret()
            log(f"Interpret result: processed={result.get('processed', 0)}, created={result.get('created', 0)}")
            
            if result.get('errors'):
                for err in result.get('errors', []):
                    log(f"  Erreur email: {err[:300]}", level='error')
            
            save_last_run()
            log("save_last_run done")
            
            # Commit final pour libérer la session
            db.session.commit()
            log("db.session.commit done")
            
        except Exception as e:
            db.session.rollback()
            import traceback
            log(f"ERREUR dans le cycle: {str(e)}")
            log(f"Traceback: {traceback.format_exc()[:500]}")
            # Continuer quand même
            try:
                db.session.rollback()
            except:
                pass
    
    return True  # Continuer

def main():
    """Point d'entrée principal - pattern DND avec redémarrage automatique."""
    global running
    
    log("="*50)
    log("Watchdog GMAO démarré (pattern DND)")
    log("="*50)
    
    # Capturer le signal d'arrêt
    def signal_handler(signum, frame):
        global running
        running = False
        log("Signal reçu, arrêt du watchdog...")
    
    signal.signal(signal.SIGTERM, signal_handler)
    signal.signal(signal.SIGINT, signal_handler)
    
    while running:
        result = None
        try:
            result = run_watchdog()
            if result is False:
                log("Watchdog désactivé - arrêt")
                break  # Sortir proprement si désactivé
        except Exception as e:
            import traceback
            log(f"Crash: {str(e)}", level='error')
            log(f"Traceback: {traceback.format_exc()[-500:]}", level='error')
            # Redémarrer automatiquement après crash
        
        # Attendre avant la prochaine vérification
        # Si le watchdog est désactivé, on attend plus longtemps pour éviter les redémarrages incessants
        wait_seconds = 300 if result is False else 30
        for _ in range(wait_seconds):
            if not running:
                break
            time.sleep(1)
    
    log("Watchdog GMAO arrêté")

if __name__ == '__main__':
    main()
