#!/usr/bin/env python3
"""
Email Organizer Bot - Automatyczna segregacja emaili (refactored slim version)
Deleguje całą logikę do modułów w llmass/*
"""
import os
import sys
import logging
import argparse
from typing import List, Dict, Tuple, Set
from datetime import datetime, timedelta
from dotenv import load_dotenv

# Import modułów llmass
from llmass.imap import connect_imap
from llmass.organizer.parser import get_email_content, detect_imap_server
from llmass.organizer.folders import FolderManager
from llmass.organizer.corruption import check_and_handle_corruption
from llmass.organizer.fetcher import fetch_and_filter
from llmass.organizer.actions import move_email as _move_email
from llmass.organizer.filters import is_spam, has_sufficient_text, get_sent_drafts_message_ids
from llmass.organizer.categorize import categorize_emails as _categorize, generate_category_name
from llmass.organizer.text_utils import make_vectorizer
from llmass.organizer.repair import repair_mailbox as _repair


class EmailOrganizer:
    """Główny interfejs organizera - deleguje do modułów llmass/*."""
    
    def __init__(self, email_address: str, password: str, imap_server: str = None,
                 similarity_threshold: float = None, min_cluster_size: int = None,
                 min_cluster_fraction: float = None, dry_run: bool = None, verbose: bool = False):
        """Inicjalizacja z konfiguracją."""
        self.email_address = email_address
        self.password = password
        self.imap_server = imap_server or detect_imap_server(email_address)
        self.imap = None
        self.client = None
        self._delim_cache = None
        
        # Config z ENV lub argumentów
        self.similarity_threshold = similarity_threshold or float(os.getenv('SIMILARITY_THRESHOLD', '0.25'))
        self.min_cluster_size = min_cluster_size or int(os.getenv('MIN_CLUSTER_SIZE', '2'))
        self.min_cluster_fraction = min_cluster_fraction or float(os.getenv('MIN_CLUSTER_FRACTION', '0.10'))
        self.cross_spam_similarity = float(os.getenv('CROSS_SPAM_SIMILARITY', '0.6'))
        self.cross_spam_sample_limit = int(os.getenv('CROSS_SPAM_SAMPLE_LIMIT', '200'))
        self.category_match_similarity = float(os.getenv('CATEGORY_MATCH_SIMILARITY', '0.5'))
        self.category_sender_weight = float(os.getenv('CATEGORY_SENDER_WEIGHT', '0.2'))
        self.category_sample_limit = int(os.getenv('CATEGORY_SAMPLE_LIMIT', '50'))
        self.cleanup_empty_categories = os.getenv('CLEANUP_EMPTY_CATEGORY_FOLDERS', 'true').lower() in ('1', 'true', 'yes')
        self.dry_run = dry_run if dry_run is not None else (os.getenv('DRY_RUN', '').lower() in ('1', 'true', 'yes'))
        self.content_min_chars = int(os.getenv('CONTENT_MIN_CHARS', '40'))
        self.content_min_tokens = int(os.getenv('CONTENT_MIN_TOKENS', '6'))
        self.conversation_history_days = int(os.getenv('CONVERSATION_HISTORY_DAYS', '360'))
        self.conversation_history_limit = int(os.getenv('CONVERSATION_HISTORY_LIMIT', '300'))
        self.imap_retries = int(os.getenv('IMAP_RETRIES', '2'))
        self.imap_backoff = float(os.getenv('IMAP_BACKOFF', '0.5'))
        self.use_sequence_numbers = False
        self.verbose = verbose
        self.tfidf_max_features = int(os.getenv('TFIDF_MAX_FEATURES', '100'))
        self.stopwords_mode = (os.getenv('STOPWORDS', 'none') or 'none').lower()
        
        # Logger
        self.logger = logging.getLogger('email_organizer')
        level = logging.DEBUG if self.verbose else logging.ERROR
        self.logger.setLevel(level)
        if not self.logger.handlers:
            ch = logging.StreamHandler()
            ch.setLevel(level)
            ch.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(message)s'))
            self.logger.addHandler(ch)
        
        # Vectorizer
        self.vectorizer = make_vectorizer(self)
        
        # Folder manager
        try:
            self.folders = FolderManager(self)
        except Exception:
            self.folders = None
        
        # Tracking
        self._current_selected_folder: str = None
        self._changed_folders: Set[str] = set()
        self._counts_before: Dict[str, int] = {}
        self._counts_after: Dict[str, int] = {}
    
    # --- Delegacje do modułów ---
    def get_email_content(self, msg) -> Dict:
        return get_email_content(msg)
    
    def is_spam(self, email_content: Dict) -> bool:
        return is_spam(email_content)
    
    def _has_sufficient_text(self, email_content: Dict) -> bool:
        return has_sufficient_text(self, email_content)
    
    def _get_sent_drafts_message_ids(self) -> set:
        return get_sent_drafts_message_ids(self)
    
    def categorize_emails(self, emails: List[Dict]) -> Dict[str, List[int]]:
        return _categorize(self, emails)
    
    def move_email(self, email_id: str, target_folder: str):
        return _move_email(self, email_id, target_folder)
    
    def repair_mailbox(self, folder: str = 'INBOX', force: bool = False, dry_run: bool = False):
        return _repair(self, folder=folder, force=force, dry_run=dry_run)
    
    # --- FolderManager delegacje ---
    def get_folders(self) -> List[str]:
        return self.folders.get_folders() if self.folders else []
    
    def print_mailbox_structure(self, max_items: int = 500, folder_counts_before: Dict[str, int] = None,
                                folder_counts_after: Dict[str, int] = None, restrict_to: List[str] = None,
                                skip_archives: bool = True):
        if self.folders:
            self.folders.print_mailbox_structure(max_items, folder_counts_before, folder_counts_after, restrict_to, skip_archives)
    
    def create_folder(self, folder_name: str):
        if self.folders:
            self.folders.create_folder(folder_name)
    
    def subscribe_folder(self, folder_name: str):
        if self.folders:
            self.folders.subscribe_folder(folder_name)
    
    def _resolve_spam_folder_name(self) -> str:
        return self.folders._resolve_spam_folder_name() if self.folders else 'INBOX/SPAM'
    
    def _resolve_category_folder_name(self, base_name: str) -> str:
        return self.folders._resolve_category_folder_name(base_name) if self.folders else base_name
    
    def _cleanup_empty_category_folders(self):
        if self.folders:
            self.folders._cleanup_empty_category_folders()
    
    def _migrate_unsafe_category_folders(self):
        if self.folders:
            self.folders._migrate_unsafe_category_folders()
    
    # --- Tracking helpers ---
    def _count_messages_in_folder(self, folder: str) -> int:
        try:
            if not folder:
                return 0
            self.client.safe_select(folder, readonly=True)
            res, data = self.client.safe_uid('SEARCH', None, 'ALL')
            if res == 'OK' and data and data[0]:
                return len(data[0].split())
        except Exception:
            pass
        finally:
            try:
                self.client.safe_select('INBOX', readonly=True)
            except Exception:
                pass
        return 0
    
    def _mark_folder_changed(self, folder: str) -> None:
        if not folder:
            return
        if folder not in self._changed_folders:
            try:
                self._counts_before[folder] = self._count_messages_in_folder(folder)
            except Exception:
                self._counts_before[folder] = None
        self._changed_folders.add(folder)
    
    def _finalize_counts_and_print(self):
        if not self.folders or not self._changed_folders:
            return
        if not self.dry_run:
            for f in list(self._changed_folders):
                try:
                    self._counts_after[f] = self._count_messages_in_folder(f)
                except Exception:
                    self._counts_after[f] = None
        try:
            self.print_mailbox_structure(500, self._counts_before,
                                        self._counts_after if not self.dry_run else None,
                                        sorted(self._changed_folders), True)
        except Exception:
            pass
    
    # --- Connection ---
    def connect(self):
        try:
            self.client, self.imap = connect_imap(self.imap_server, self.email_address, self.password,
                                                   ssl=True, retries=self.imap_retries,
                                                   backoff=self.imap_backoff, verbose=self.verbose)
            if self.verbose:
                print(f"✅ Połączono z {self.imap_server}")
            try:
                self._delim_cache = self.folders._get_hierarchy_delimiter() if self.folders else '/'
            except Exception:
                self._delim_cache = '/'
            return True
        except Exception as e:
            print(f"❌ Błąd połączenia: {e}")
            return False
    
    def disconnect(self):
        if self.imap:
            self.imap.close()
            self.imap.logout()
            if self.verbose:
                print("👋 Rozłączono z serwerem")
    
    # --- Main logic ---
    def organize_mailbox(self, limit: int = 100, since_days: int = 7, since_date: str = None,
                        folder: str = None, include_subfolders: bool = False):
        """Główna funkcja organizująca skrzynkę - deleguje do modułów."""
        if self.verbose:
            print("\n🔄 Rozpoczynam organizację skrzynki email...")
            if self.dry_run:
                print("🧪 Tryb DRY-RUN: nie będzie faktycznych zmian")
        
        self._migrate_unsafe_category_folders()
        self._cleanup_empty_category_folders()
        self.print_mailbox_structure()
        
        spam_folder = self._resolve_spam_folder_name()
        if self.verbose:
            print(f"📦 Docelowy folder SPAM/Junk: {spam_folder}")
        
        folders = self.get_folders()
        if self.verbose:
            print(f"📊 Znaleziono {len(folders)} folderów")
        
        selected_folder = folder or 'INBOX'
        self.logger.debug(f"Selecting folder: {selected_folder} (include_subfolders={include_subfolders})")
        
        result, data = self.client.safe_select(selected_folder, readonly=False)
        self._current_selected_folder = selected_folder
        if result != 'OK':
            print(f"❌ Nie można otworzyć folderu {selected_folder}")
            return
        
        # EXPUNGE
        try:
            result = self.client.safe_expunge()
            if result[0] == 'OK' and result[1] and result[1][0]:
                if self.verbose:
                    print(f"🗑️  EXPUNGE usunięto {len(result[1])} emaili")
            else:
                self.logger.debug("EXPUNGE wykonano - brak emaili do usunięcia")
        except Exception as e:
            if self.verbose:
                print(f"⚠️  EXPUNGE failed: {e}")
        
        # Pobierz UIDs
        result, data = self.client.safe_uid('SEARCH', None, 'ALL')
        if result != 'OK' or not data or not data[0]:
            if self.verbose:
                print("📭 Brak emaili w folderze")
            return
        
        email_ids = data[0].split()
        if not email_ids:
            if self.verbose:
                print("📭 Brak emaili do przetworzenia")
            return
        
        # Filtruj po dacie jeśli podano
        if since_date or since_days:
            if since_date:
                cutoff = datetime.strptime(since_date, '%Y-%m-%d')
            else:
                cutoff = datetime.now() - timedelta(days=since_days)
            
            search_str = cutoff.strftime('%d-%b-%Y')
            result, data = self.client.safe_uid('SEARCH', None, f'SINCE {search_str}')
            if result == 'OK' and data and data[0]:
                email_ids = data[0].split()
        
        # Limit
        if limit and len(email_ids) > limit:
            if self.verbose:
                print(f"🔍 PRZED LIMITEM: Pierwszy UID: {email_ids[0]}, Ostatni UID: {email_ids[-1]}")
            email_ids = email_ids[-limit:]
            if self.verbose:
                print(f"🔍 PO LIMICIE: Pierwszy UID: {email_ids[0]}, Ostatni UID: {email_ids[-1]}")
        
        # Corruption check
        corruption_ratio = check_and_handle_corruption(self, email_ids)
        
        if self.verbose:
            print(f"📥 Znaleziono {len(email_ids)} emaili do analizy")
        
        # Get sent/drafts IDs
        if self.verbose:
            print("🔍 Sprawdzam aktywne konwersacje (Sent/Drafts)...")
        sent_drafts_ids = self._get_sent_drafts_message_ids()
        if sent_drafts_ids and self.verbose:
            print(f"   Znaleziono {len(sent_drafts_ids)} wiadomości w aktywnych konwersacjach")
        
        # Restore folder
        try:
            self.client.safe_select(selected_folder, readonly=False)
            self._current_selected_folder = selected_folder
        except Exception:
            pass
        
        # Fetch and filter
        emails_data, stats = fetch_and_filter(self, email_ids, limit, sent_drafts_ids)
        if self.verbose:
            print()
            print(f"📊 Statystyki analizy:")
            print(f"   • Przeanalizowano: {stats.get('scanned', len(email_ids))} emaili")
            print(f"   • Do kategoryzacji: {len(emails_data)} emaili")
            print(f"   • Spam: {stats.get('spam', 0)} emaili")
            print(f"   • Krótkie wiadomości: {stats.get('short', 0)} emaili")
            print(f"   • Aktywne konwersacje: {stats.get('active_conv', 0)} emaili")
            print(f"   • Pominięto (niska treść): {stats.get('skipped_low_text', 0)} emaili")
        
        # Move spam
        spam_ids = stats.get('spam_ids', [])
        if spam_ids and spam_folder:
            if self.verbose:
                print(f"\n🗑️  Przenoszę {len(spam_ids)} emaili spam do {spam_folder}...")
            moved_count = 0
            for spam_id in spam_ids:
                if self.move_email(spam_id, spam_folder):
                    moved_count += 1
            if self.verbose:
                if self.dry_run:
                    print(f"🧪 [DRY-RUN] Przeniosłbym {moved_count} emaili spam")
                else:
                    print(f"✅ Przeniesiono {moved_count}/{len(spam_ids)} emaili spam")
        
        if not emails_data:
            if self.verbose:
                print("📭 Brak emaili do kategoryzacji po filtrowaniu")
            self._finalize_counts_and_print()
            return
        
        # Categorize
        if self.verbose:
            print("🤖 Rozpoczynam kategoryzację AI...")
        
        categories = self.categorize_emails(emails_data)
        if not categories:
            if self.verbose:
                print("📭 Nie znaleziono wystarczających grup do kategoryzacji")
            self._finalize_counts_and_print()
            return
        
        if self.verbose:
            print(f"\n📁 Znaleziono {len(categories)} kategorii:")
            for cat_name, indices in categories.items():
                print(f"   • {cat_name}: {len(indices)} emaili")
        
        # Move to categories
        total_moved = 0
        for category_name, email_indices in categories.items():
            safe_folder = self._resolve_category_folder_name(category_name)
            if safe_folder not in folders:
                self.create_folder(safe_folder)
                self.subscribe_folder(safe_folder)
                folders.append(safe_folder)
            
            moved_in_category = 0
            for idx in email_indices:
                email = emails_data[idx]
                email_id = email.get('id')
                if email_id and self.move_email(email_id, safe_folder):
                    moved_in_category += 1
                    total_moved += 1
            
            if self.verbose:
                if self.dry_run:
                    print(f"   🧪 [DRY-RUN] {safe_folder}: przeniosłbym {moved_in_category} emaili")
                else:
                    print(f"   ✅ {safe_folder}: przeniesiono {moved_in_category} emaili")
        
        if self.verbose:
            if self.dry_run:
                print(f"\n📊 Łącznie: przeniosłbym {total_moved} emaili do {len(categories)} kategorii")
            else:
                print(f"\n📊 Łącznie: przeniesiono {total_moved} emaili do {len(categories)} kategorii")
        
        if self.cleanup_empty_categories:
            self._cleanup_empty_category_folders()
        
        self._finalize_counts_and_print()


def main():
    load_dotenv()
    parser = argparse.ArgumentParser(description='Email Organizer Bot')
    parser.add_argument('--email', required=False, default=None, help='Adres email')
    parser.add_argument('--password', required=False, default=None, help='Hasło do skrzynki')
    parser.add_argument('--server', required=False, default=None, help='Serwer IMAP')
    parser.add_argument('--dry-run', action='store_true', help='Tylko analizuj, nie przenoś emaili')
    parser.add_argument('--limit', type=int, default=None, help='Limit emaili (domyślnie 100)')
    parser.add_argument('--since-days', type=int, default=None, help='Ile dni wstecz (domyślnie 7)')
    parser.add_argument('--since-date', type=str, default=None, help='Data YYYY-MM-DD')
    parser.add_argument('--similarity-threshold', type=float, default=None, help='Próg podobieństwa (0-1)')
    parser.add_argument('--min-cluster-size', type=int, default=None, help='Min liczba emaili w klastrze')
    parser.add_argument('--min-cluster-fraction', type=float, default=None, help='Min ułamek w klastrze')
    parser.add_argument('--folder', type=str, default=None, help='Folder (domyślnie INBOX)')
    parser.add_argument('--include-subfolders', action='store_true', help='Przetwarzaj podfoldery')
    parser.add_argument('--repair', action='store_true', help='Napraw corruption UIDs')
    parser.add_argument('--force', action='store_true', help='Wymusza naprawę bez potwierdzenia')
    parser.add_argument('--verbose', '-v', action='store_true', help='Tryb verbose')
    
    args = parser.parse_args()
    
    email_arg = args.email or os.getenv('EMAIL_ADDRESS')
    password_arg = args.password or os.getenv('EMAIL_PASSWORD')
    server_arg = args.server or os.getenv('IMAP_SERVER')
    limit_arg = args.limit if args.limit is not None else int(os.getenv('LIMIT', '100'))
    since_days_arg = args.since_days if args.since_days is not None else int(os.getenv('SINCE_DAYS', '7'))
    since_date_arg = args.since_date if args.since_date is not None else os.getenv('SINCE_DATE', None)
    
    if not email_arg or not password_arg:
        print("❌ Brak wymaganych danych logowania. Podaj --email/--password lub .env")
        sys.exit(1)
    
    bot = EmailOrganizer(email_arg, password_arg, server_arg,
                        similarity_threshold=args.similarity_threshold,
                        min_cluster_size=args.min_cluster_size,
                        min_cluster_fraction=args.min_cluster_fraction,
                        dry_run=args.dry_run if hasattr(args, 'dry_run') else None,
                        verbose=args.verbose if hasattr(args, 'verbose') else False)
    
    if bot.connect():
        try:
            if args.repair:
                bot.repair_mailbox(folder=args.folder or 'INBOX', force=args.force, dry_run=args.dry_run)
            else:
                bot.organize_mailbox(limit=limit_arg, since_days=since_days_arg, since_date=since_date_arg,
                                    folder=args.folder, include_subfolders=args.include_subfolders)
        finally:
            bot.disconnect()


if __name__ == "__main__":
    main()
