#!/usr/bin/env python3
"""
Gmail Manager - Backup e Limpeza de Emails
==========================================
Script Python para gerenciar Gmail via API.
Sem limite de tempo (diferente do Google Apps Script).

Uso:
    python3 gmail_manager.py backup      # Backup completo
    python3 gmail_manager.py analyze     # Analisar inbox
    python3 gmail_manager.py cleanup     # Limpar (com backup)
    python3 gmail_manager.py --help      # Ajuda

Autor: NEOG Workspace
Data: 2025-01-11
"""

import os
import sys
import json
import base64
import pickle
import argparse
from datetime import datetime, timedelta
from pathlib import Path
from typing import Optional, List, Dict, Any

# Google API imports
try:
    from google.auth.transport.requests import Request
    from google.oauth2.credentials import Credentials
    from google_auth_oauthlib.flow import InstalledAppFlow
    from googleapiclient.discovery import build
    from googleapiclient.errors import HttpError
except ImportError:
    print("❌ Dependências não instaladas. Execute:")
    print("   pip3 install google-api-python-client google-auth-oauthlib")
    sys.exit(1)

# Configurações
SCRIPT_DIR = Path(__file__).parent
CREDENTIALS_FILE = SCRIPT_DIR / "credentials.json"
TOKEN_FILE = SCRIPT_DIR / "token.pickle"
BACKUP_DIR = SCRIPT_DIR / "backups"
SCOPES = [
    'https://www.googleapis.com/auth/gmail.readonly',
    'https://www.googleapis.com/auth/gmail.modify',
    'https://www.googleapis.com/auth/gmail.labels'
]

# Configurações de limpeza
CONFIG = {
    'dias_newsletters': 30,
    'dias_promocoes': 14,
    'dias_social': 7,
    'dias_updates': 30,
    'max_emails': 500,  # Sem limite real, mas para controle
    'preview_mode': True,  # True = não deleta nada
}

# Categorias para limpeza
CLEANUP_QUERIES = {
    'newsletters': 'category:promotions OR (unsubscribe OR "cancelar inscrição")',
    'social': 'category:social',
    'updates': 'category:updates',
    'forums': 'category:forums',
}


class GmailManager:
    """Gerenciador de Gmail via API."""

    def __init__(self):
        self.service = None
        self.user_email = None

    def authenticate(self) -> bool:
        """Autentica com o Gmail API."""
        creds = None

        # Carregar token existente
        if TOKEN_FILE.exists():
            with open(TOKEN_FILE, 'rb') as token:
                creds = pickle.load(token)

        # Se não há credenciais válidas, fazer login
        if not creds or not creds.valid:
            if creds and creds.expired and creds.refresh_token:
                print("🔄 Renovando token...")
                creds.refresh(Request())
            else:
                if not CREDENTIALS_FILE.exists():
                    print(f"❌ Arquivo de credenciais não encontrado: {CREDENTIALS_FILE}")
                    return False

                print("🔐 Abrindo navegador para autenticação...")
                print("   Faça login com sua conta Google e autorize o acesso.\n")

                flow = InstalledAppFlow.from_client_secrets_file(
                    str(CREDENTIALS_FILE), SCOPES
                )
                creds = flow.run_local_server(port=9999)

            # Salvar token para próximas execuções
            with open(TOKEN_FILE, 'wb') as token:
                pickle.dump(creds, token)
            print("✅ Token salvo para próximas execuções.\n")

        # Criar serviço Gmail
        self.service = build('gmail', 'v1', credentials=creds)

        # Obter email do usuário
        profile = self.service.users().getProfile(userId='me').execute()
        self.user_email = profile.get('emailAddress')
        print(f"✅ Conectado como: {self.user_email}\n")

        return True

    def get_messages(self, query: str = '', max_results: int = 500) -> List[Dict]:
        """Busca mensagens com uma query."""
        messages = []
        page_token = None

        while len(messages) < max_results:
            try:
                results = self.service.users().messages().list(
                    userId='me',
                    q=query,
                    maxResults=min(100, max_results - len(messages)),
                    pageToken=page_token
                ).execute()

                batch = results.get('messages', [])
                messages.extend(batch)

                page_token = results.get('nextPageToken')
                if not page_token:
                    break

                print(f"   📥 {len(messages)} mensagens encontradas...", end='\r')

            except HttpError as e:
                print(f"❌ Erro na API: {e}")
                break

        print(f"   📥 {len(messages)} mensagens encontradas.   ")
        return messages

    def get_message_details(self, msg_id: str) -> Optional[Dict]:
        """Obtém detalhes de uma mensagem."""
        try:
            msg = self.service.users().messages().get(
                userId='me',
                id=msg_id,
                format='full'
            ).execute()

            # Extrair headers
            headers = {h['name']: h['value'] for h in msg['payload'].get('headers', [])}

            # Extrair corpo
            body = ''
            if 'parts' in msg['payload']:
                for part in msg['payload']['parts']:
                    if part['mimeType'] == 'text/plain':
                        data = part['body'].get('data', '')
                        if data:
                            body = base64.urlsafe_b64decode(data).decode('utf-8', errors='ignore')
                            break
            elif 'body' in msg['payload']:
                data = msg['payload']['body'].get('data', '')
                if data:
                    body = base64.urlsafe_b64decode(data).decode('utf-8', errors='ignore')

            return {
                'id': msg['id'],
                'threadId': msg['threadId'],
                'date': headers.get('Date', ''),
                'from': headers.get('From', ''),
                'to': headers.get('To', ''),
                'subject': headers.get('Subject', ''),
                'body': body[:5000],  # Limitar tamanho
                'labels': msg.get('labelIds', []),
                'snippet': msg.get('snippet', '')
            }

        except HttpError as e:
            print(f"⚠️ Erro ao obter mensagem {msg_id}: {e}")
            return None

    def analyze_inbox(self) -> Dict[str, Any]:
        """Analisa o inbox e retorna estatísticas."""
        print("📊 Analisando inbox...\n")

        stats = {
            'total': 0,
            'unread': 0,
            'newsletters': 0,
            'social': 0,
            'updates': 0,
            'forums': 0,
            'promotions': 0,
        }

        # Total na inbox
        inbox = self.get_messages('in:inbox')
        stats['total'] = len(inbox)

        # Não lidos
        unread = self.get_messages('is:unread in:inbox')
        stats['unread'] = len(unread)

        # Por categoria
        for category in ['newsletters', 'social', 'updates', 'forums', 'promotions']:
            query = f'category:{category}' if category != 'newsletters' else CLEANUP_QUERIES['newsletters']
            msgs = self.get_messages(f'{query} older_than:{CONFIG["dias_newsletters"]}d')
            stats[category] = len(msgs)

        # Exibir relatório
        print(f"""
{'='*50}
📊 ANÁLISE DO GMAIL
{'='*50}

📬 Total na Inbox: {stats['total']}
📭 Não lidos: {stats['unread']}

🗑️ CANDIDATOS À LIMPEZA (emails antigos):
├── 📰 Newsletters/Promoções: {stats['newsletters']}
├── 👥 Social: {stats['social']}
├── 🔔 Updates: {stats['updates']}
├── 💬 Forums: {stats['forums']}
└── 🏷️ Promotions: {stats['promotions']}

{'='*50}
Total para limpar: {sum([stats['newsletters'], stats['social'], stats['updates'], stats['forums']])}
{'='*50}
""")

        return stats

    def backup_emails(self, query: str = 'in:inbox', max_emails: int = 1000) -> str:
        """Faz backup de emails para JSON."""
        print(f"💾 Iniciando backup...")
        print(f"   Query: {query}")
        print(f"   Máximo: {max_emails} emails\n")

        # Criar pasta de backup
        BACKUP_DIR.mkdir(exist_ok=True)

        # Buscar mensagens
        messages = self.get_messages(query, max_emails)

        if not messages:
            print("ℹ️ Nenhum email encontrado.")
            return None

        # Coletar detalhes
        backup_data = []
        total = len(messages)

        for i, msg in enumerate(messages):
            print(f"   📧 Processando {i+1}/{total}...", end='\r')
            details = self.get_message_details(msg['id'])
            if details:
                backup_data.append(details)

        print(f"   📧 Processados {len(backup_data)} emails.   \n")

        # Salvar backup
        timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
        backup_file = BACKUP_DIR / f"gmail_backup_{timestamp}.json"

        with open(backup_file, 'w', encoding='utf-8') as f:
            json.dump(backup_data, f, ensure_ascii=False, indent=2)

        # Também salvar CSV para fácil visualização
        csv_file = BACKUP_DIR / f"gmail_backup_{timestamp}.csv"
        with open(csv_file, 'w', encoding='utf-8') as f:
            f.write("ID,Data,De,Para,Assunto,Labels\n")
            for email in backup_data:
                row = [
                    email['id'],
                    email['date'],
                    email['from'].replace(',', ';'),
                    email['to'].replace(',', ';') if email['to'] else '',
                    email['subject'].replace(',', ';').replace('\n', ' ')[:100],
                    '|'.join(email['labels'])
                ]
                f.write(','.join([f'"{c}"' for c in row]) + '\n')

        print(f"""
{'='*50}
💾 BACKUP CONCLUÍDO!
{'='*50}

📊 Emails salvos: {len(backup_data)}
📁 JSON: {backup_file}
📄 CSV: {csv_file}

{'='*50}
""")

        return str(backup_file)

    def trash_messages(self, msg_ids: List[str], preview: bool = True) -> int:
        """Move mensagens para lixeira."""
        if preview:
            print(f"   [PREVIEW] {len(msg_ids)} mensagens seriam movidas para lixeira")
            return len(msg_ids)

        count = 0
        for msg_id in msg_ids:
            try:
                self.service.users().messages().trash(
                    userId='me',
                    id=msg_id
                ).execute()
                count += 1
            except HttpError as e:
                print(f"⚠️ Erro ao mover {msg_id}: {e}")

        return count

    def cleanup_with_backup(self) -> Dict[str, Any]:
        """Executa limpeza com backup prévio."""
        print("🛡️ LIMPEZA SEGURA COM BACKUP\n")

        results = {
            'backup_file': None,
            'cleaned': {}
        }

        # 1. Primeiro fazer backup dos emails que serão deletados
        print("Passo 1: Fazendo backup dos emails a serem limpos...\n")

        all_to_clean = []
        for category, query in CLEANUP_QUERIES.items():
            days = CONFIG.get(f'dias_{category}', 30)
            full_query = f'{query} older_than:{days}d'
            msgs = self.get_messages(full_query, 200)
            all_to_clean.extend([m['id'] for m in msgs])

        if all_to_clean:
            # Backup
            backup_data = []
            for msg_id in all_to_clean[:500]:  # Limitar backup
                details = self.get_message_details(msg_id)
                if details:
                    backup_data.append(details)

            timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
            backup_file = BACKUP_DIR / f"cleanup_backup_{timestamp}.json"
            BACKUP_DIR.mkdir(exist_ok=True)

            with open(backup_file, 'w', encoding='utf-8') as f:
                json.dump(backup_data, f, ensure_ascii=False, indent=2)

            results['backup_file'] = str(backup_file)
            print(f"   ✅ Backup salvo: {backup_file}\n")

        # 2. Executar limpeza
        print("Passo 2: Executando limpeza...\n")

        for category, query in CLEANUP_QUERIES.items():
            days = CONFIG.get(f'dias_{category}', 30)
            full_query = f'{query} older_than:{days}d'

            print(f"   🧹 Limpando {category}...")
            msgs = self.get_messages(full_query, 100)
            msg_ids = [m['id'] for m in msgs]

            cleaned = self.trash_messages(msg_ids, preview=CONFIG['preview_mode'])
            results['cleaned'][category] = cleaned

        # Relatório
        total_cleaned = sum(results['cleaned'].values())

        print(f"""
{'='*50}
{'🎉 LIMPEZA CONCLUÍDA!' if not CONFIG['preview_mode'] else '📋 PREVIEW DA LIMPEZA'}
{'='*50}

💾 Backup: {results['backup_file'] or 'N/A'}

🧹 Emails processados:
├── 📰 Newsletters: {results['cleaned'].get('newsletters', 0)}
├── 👥 Social: {results['cleaned'].get('social', 0)}
├── 🔔 Updates: {results['cleaned'].get('updates', 0)}
└── 💬 Forums: {results['cleaned'].get('forums', 0)}

📊 Total: {total_cleaned}
{f"⚠️ MODO PREVIEW - Nenhum email foi deletado" if CONFIG['preview_mode'] else "✅ Emails movidos para lixeira"}

{'='*50}
""")

        return results


def main():
    """Função principal."""
    parser = argparse.ArgumentParser(
        description='Gmail Manager - Backup e Limpeza de Emails',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Exemplos:
  python3 gmail_manager.py analyze          # Ver estatísticas
  python3 gmail_manager.py backup           # Backup do inbox
  python3 gmail_manager.py backup --query "from:amazon"  # Backup filtrado
  python3 gmail_manager.py cleanup          # Limpar (preview)
  python3 gmail_manager.py cleanup --execute  # Limpar de verdade
        """
    )

    parser.add_argument('action', choices=['analyze', 'backup', 'cleanup'],
                        help='Ação a executar')
    parser.add_argument('--query', '-q', default='in:inbox',
                        help='Query de busca para backup')
    parser.add_argument('--max', '-m', type=int, default=500,
                        help='Máximo de emails para processar')
    parser.add_argument('--execute', '-e', action='store_true',
                        help='Executar limpeza de verdade (não apenas preview)')

    args = parser.parse_args()

    # Configurar modo
    if args.execute:
        CONFIG['preview_mode'] = False
        print("⚠️ MODO EXECUÇÃO - Emails serão realmente deletados!\n")

    # Inicializar
    gmail = GmailManager()

    if not gmail.authenticate():
        sys.exit(1)

    # Executar ação
    if args.action == 'analyze':
        gmail.analyze_inbox()

    elif args.action == 'backup':
        gmail.backup_emails(query=args.query, max_emails=args.max)

    elif args.action == 'cleanup':
        gmail.cleanup_with_backup()


if __name__ == '__main__':
    main()
