#!/usr/bin/env python3
"""
NetGescon Importer - Advanced Bridge v2.0
Importazione dati da GESCON legacy a NetGescon Laravel

Author: NetGescon Development Team
Date: 15 Luglio 2025
"""

import os
import sys
import json
import logging
import sqlite3
import pandas as pd
import requests
import schedule
import time
from datetime import datetime, timedelta
from pathlib import Path
from typing import Dict, List, Any, Optional, Tuple
from dataclasses import dataclass, asdict
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

# === CONFIGURATION CLASSES ===

@dataclass
class GesconConfig:
    """Configurazione connessione GESCON"""
    database_path: str
    backup_frequency: str = "daily"
    read_only: bool = True
    timeout: int = 30

@dataclass
class NetGesconConfig:
    """Configurazione API NetGescon"""
    base_url: str
    api_token: str
    api_version: str = "v1"
    timeout: int = 60
    max_retries: int = 3

@dataclass
class ImportConfig:
    """Configurazione importazione"""
    batch_size: int = 100
    enable_validation: bool = True
    create_backups: bool = True
    sync_mode: str = "incremental"  # full, incremental
    log_level: str = "INFO"

# === MAIN IMPORTER CLASS ===

class NetGesconImporter:
    """
    Importer principale per sincronizzazione GESCON → NetGescon
    """
    
    def __init__(self, config_file: str):
        self.config = self._load_config(config_file)
        self.gescon_db = None
        self.session = None
        self.logger = self._setup_logging()
        self.stats = {
            'stabili': {'imported': 0, 'errors': 0},
            'unita': {'imported': 0, 'errors': 0},
            'soggetti': {'imported': 0, 'errors': 0},
            'movimenti': {'imported': 0, 'errors': 0}
        }
        
    def _load_config(self, config_file: str) -> Dict[str, Any]:
        """Carica configurazione da file JSON"""
        try:
            with open(config_file, 'r', encoding='utf-8') as f:
                config = json.load(f)
            return config
        except Exception as e:
            print(f"Errore caricamento configurazione: {e}")
            sys.exit(1)
    
    def _setup_logging(self) -> logging.Logger:
        """Configura sistema di logging"""
        log_dir = Path(self.config.get('log_directory', 'logs'))
        log_dir.mkdir(exist_ok=True)
        
        log_file = log_dir / f"netgescon_import_{datetime.now().strftime('%Y%m%d')}.log"
        
        logging.basicConfig(
            level=getattr(logging, self.config.get('log_level', 'INFO')),
            format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
            handlers=[
                logging.FileHandler(log_file, encoding='utf-8'),
                logging.StreamHandler()
            ]
        )
        
        return logging.getLogger('NetGesconImporter')
    
    def _setup_http_session(self) -> requests.Session:
        """Configura sessione HTTP con retry automatico"""
        session = requests.Session()
        
        retry_strategy = Retry(
            total=self.config['netgescon']['max_retries'],
            backoff_factor=1,
            status_forcelist=[429, 500, 502, 503, 504],
        )
        
        adapter = HTTPAdapter(max_retries=retry_strategy)
        session.mount("http://", adapter)
        session.mount("https://", adapter)
        
        # Headers comuni
        session.headers.update({
            'Authorization': f"Bearer {self.config['netgescon']['api_token']}",
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'User-Agent': 'NetGescon-Importer/2.0'
        })
        
        return session
    
    def connect_gescon(self) -> bool:
        """Connessione al database GESCON"""
        try:
            db_path = self.config['gescon']['database_path']
            self.gescon_db = sqlite3.connect(db_path, timeout=30)
            self.gescon_db.row_factory = sqlite3.Row  # Per accesso by name
            
            # Test connessione
            cursor = self.gescon_db.cursor()
            cursor.execute("SELECT COUNT(*) FROM sqlite_master WHERE type='table';")
            table_count = cursor.fetchone()[0]
            
            self.logger.info(f"Connesso a GESCON: {table_count} tabelle trovate")
            return True
            
        except Exception as e:
            self.logger.error(f"Errore connessione GESCON: {e}")
            return False
    
    def test_netgescon_connection(self) -> bool:
        """Test connessione API NetGescon"""
        try:
            url = f"{self.config['netgescon']['base_url']}/api/health"
            response = self.session.get(url, timeout=10)
            response.raise_for_status()
            
            data = response.json()
            self.logger.info(f"NetGescon API: {data.get('status', 'OK')} - {data.get('version', 'unknown')}")
            return True
            
        except Exception as e:
            self.logger.error(f"Errore connessione NetGescon API: {e}")
            return False
    
    # === MAPPING DATA METHODS ===
    
    def map_stabile_data(self, gescon_row: sqlite3.Row) -> Dict[str, Any]:
        """Mappa dati stabile da GESCON a NetGescon"""
        try:
            return {
                'codice_esterno': str(gescon_row.get('id_stabile', '')),
                'denominazione': gescon_row.get('denominazione', '').strip(),
                'indirizzo': gescon_row.get('indirizzo', '').strip(),
                'citta': gescon_row.get('citta', '').strip(),
                'cap': gescon_row.get('cap', '').strip(),
                'provincia': gescon_row.get('provincia', '').strip(),
                'codice_fiscale': gescon_row.get('codice_fiscale', '').strip(),
                'partita_iva': gescon_row.get('partita_iva', '').strip(),
                'telefono': gescon_row.get('telefono', '').strip(),
                'email': gescon_row.get('email', '').strip(),
                
                # Campi avanzati
                'numero_unita': gescon_row.get('numero_unita', 0) or 0,
                'anno_costruzione': gescon_row.get('anno_costruzione'),
                'numero_piani': gescon_row.get('numero_piani', 0) or 0,
                'superficie_totale': float(gescon_row.get('superficie_totale', 0) or 0),
                'ha_ascensore': bool(gescon_row.get('ascensore', 0)),
                'ha_riscaldamento_centralizzato': bool(gescon_row.get('riscaldamento_centrale', 0)),
                
                # Configurazioni automatiche
                'calcolo_automatico_ripartizioni': True,
                'gestione_fondi_automatica': True,
                'notifiche_attive': True,
                
                # Metadati import
                'importato_da_gescon': True,
                'data_ultima_sincronizzazione': datetime.now().isoformat(),
                'note_import': f"Importato da GESCON ID: {gescon_row.get('id_stabile')}"
            }
        except Exception as e:
            self.logger.error(f"Errore mapping stabile {gescon_row.get('id_stabile')}: {e}")
            return None
    
    def map_unita_data(self, gescon_row: sqlite3.Row, stabile_id: int) -> Dict[str, Any]:
        """Mappa dati unità immobiliare da GESCON a NetGescon"""
        try:
            return {
                'stabile_id': stabile_id,
                'codice_esterno': str(gescon_row.get('id_unita', '')),
                'denominazione': gescon_row.get('denominazione', '').strip() or f"Unità {gescon_row.get('numero_interno', '')}",
                'numero_interno': gescon_row.get('numero_interno', '').strip(),
                'piano': int(gescon_row.get('piano', 0) or 0),
                'scala': gescon_row.get('scala', '').strip(),
                'palazzina': gescon_row.get('palazzina', '').strip(),
                
                # Superfici
                'superficie_commerciale': float(gescon_row.get('superficie_commerciale', 0) or 0),
                'superficie_calpestabile': float(gescon_row.get('superficie_calpestabile', 0) or 0),
                'superficie_balconi': float(gescon_row.get('superficie_balconi', 0) or 0),
                'superficie_terrazzi': float(gescon_row.get('superficie_terrazzi', 0) or 0),
                
                # Dati tecnici
                'numero_vani': int(gescon_row.get('vani', 0) or 0),
                'numero_bagni': int(gescon_row.get('bagni', 0) or 0),
                'numero_balconi': int(gescon_row.get('balconi', 0) or 0),
                'classe_energetica': gescon_row.get('classe_energetica', '').strip(),
                'anno_costruzione': gescon_row.get('anno_costruzione'),
                'stato_conservazione': self._map_stato_conservazione(gescon_row.get('stato')),
                'necessita_lavori': bool(gescon_row.get('necessita_lavori', 0)),
                
                # Millesimi
                'millesimi_proprieta': float(gescon_row.get('millesimi_proprieta', 0) or 0),
                'millesimi_riscaldamento': float(gescon_row.get('millesimi_riscaldamento', 0) or 0),
                'millesimi_ascensore': float(gescon_row.get('millesimi_ascensore', 0) or 0),
                'millesimi_scale': float(gescon_row.get('millesimi_scale', 0) or 0),
                'millesimi_pulizie': float(gescon_row.get('millesimi_pulizie', 0) or 0),
                
                # Automazioni
                'calcolo_automatico_millesimi': True,
                'notifiche_subentri': True,
                
                # Metadati import
                'importato_da_gescon': True,
                'data_ultima_sincronizzazione': datetime.now().isoformat(),
                'note_tecniche': f"Importato da GESCON ID: {gescon_row.get('id_unita')}"
            }
        except Exception as e:
            self.logger.error(f"Errore mapping unità {gescon_row.get('id_unita')}: {e}")
            return None
    
    def map_soggetto_data(self, gescon_row: sqlite3.Row) -> Dict[str, Any]:
        """Mappa dati soggetto da GESCON a NetGescon"""
        try:
            return {
                'codice_esterno': str(gescon_row.get('id_soggetto', '')),
                'tipo_soggetto': 'persona_fisica' if gescon_row.get('tipo') == 'F' else 'persona_giuridica',
                'denominazione': gescon_row.get('denominazione', '').strip(),
                'nome': gescon_row.get('nome', '').strip(),
                'cognome': gescon_row.get('cognome', '').strip(),
                'codice_fiscale': gescon_row.get('codice_fiscale', '').strip(),
                'partita_iva': gescon_row.get('partita_iva', '').strip(),
                
                # Indirizzo
                'indirizzo': gescon_row.get('indirizzo', '').strip(),
                'citta': gescon_row.get('citta', '').strip(),
                'cap': gescon_row.get('cap', '').strip(),
                'provincia': gescon_row.get('provincia', '').strip(),
                
                # Contatti
                'telefono': gescon_row.get('telefono', '').strip(),
                'cellulare': gescon_row.get('cellulare', '').strip(),
                'email': gescon_row.get('email', '').strip(),
                'pec': gescon_row.get('pec', '').strip(),
                
                # Metadati
                'attivo': bool(gescon_row.get('attivo', 1)),
                'importato_da_gescon': True,
                'data_ultima_sincronizzazione': datetime.now().isoformat(),
                'note': f"Importato da GESCON ID: {gescon_row.get('id_soggetto')}"
            }
        except Exception as e:
            self.logger.error(f"Errore mapping soggetto {gescon_row.get('id_soggetto')}: {e}")
            return None
    
    def _map_stato_conservazione(self, stato_gescon: str) -> str:
        """Mappa stato conservazione da GESCON a NetGescon"""
        mapping = {
            'O': 'ottimo',
            'B': 'buono', 
            'D': 'discreto',
            'C': 'cattivo',
            '1': 'ottimo',
            '2': 'buono',
            '3': 'discreto',
            '4': 'cattivo'
        }
        return mapping.get(str(stato_gescon).upper(), 'buono')
    
    # === IMPORT METHODS ===
    
    def import_stabili(self) -> bool:
        """Importa tutti gli stabili da GESCON"""
        try:
            self.logger.info("Inizio importazione stabili...")
            
            cursor = self.gescon_db.cursor()
            cursor.execute("""
                SELECT * FROM stabili 
                WHERE attivo = 1 
                ORDER BY id_stabile
            """)
            
            stabili = cursor.fetchall()
            self.logger.info(f"Trovati {len(stabili)} stabili da importare")
            
            for stabile_row in stabili:
                try:
                    stabile_data = self.map_stabile_data(stabile_row)
                    if not stabile_data:
                        continue
                    
                    # Invia a NetGescon
                    success = self._send_to_netgescon('stabili', stabile_data)
                    
                    if success:
                        self.stats['stabili']['imported'] += 1
                        self.logger.debug(f"Stabile {stabile_data['denominazione']} importato")
                    else:
                        self.stats['stabili']['errors'] += 1
                        
                except Exception as e:
                    self.logger.error(f"Errore importazione stabile {stabile_row.get('id_stabile')}: {e}")
                    self.stats['stabili']['errors'] += 1
            
            self.logger.info(f"Importazione stabili completata: {self.stats['stabili']['imported']} successi, {self.stats['stabili']['errors']} errori")
            return True
            
        except Exception as e:
            self.logger.error(f"Errore importazione stabili: {e}")
            return False
    
    def import_unita_immobiliari(self) -> bool:
        """Importa tutte le unità immobiliari da GESCON"""
        try:
            self.logger.info("Inizio importazione unità immobiliari...")
            
            cursor = self.gescon_db.cursor()
            cursor.execute("""
                SELECT u.*, s.id_stabile_netgescon
                FROM unita_immobiliari u
                JOIN stabili s ON u.id_stabile = s.id_stabile
                WHERE u.attiva = 1 AND s.importato_netgescon = 1
                ORDER BY u.id_stabile, u.id_unita
            """)
            
            unita = cursor.fetchall()
            self.logger.info(f"Trovate {len(unita)} unità immobiliari da importare")
            
            for unita_row in unita:
                try:
                    unita_data = self.map_unita_data(unita_row, unita_row['id_stabile_netgescon'])
                    if not unita_data:
                        continue
                    
                    # Invia a NetGescon
                    success = self._send_to_netgescon('unita-immobiliari', unita_data)
                    
                    if success:
                        self.stats['unita']['imported'] += 1
                        self.logger.debug(f"Unità {unita_data['denominazione']} importata")
                    else:
                        self.stats['unita']['errors'] += 1
                        
                except Exception as e:
                    self.logger.error(f"Errore importazione unità {unita_row.get('id_unita')}: {e}")
                    self.stats['unita']['errors'] += 1
            
            self.logger.info(f"Importazione unità completata: {self.stats['unita']['imported']} successi, {self.stats['unita']['errors']} errori")
            return True
            
        except Exception as e:
            self.logger.error(f"Errore importazione unità: {e}")
            return False
    
    def _send_to_netgescon(self, endpoint: str, data: Dict[str, Any]) -> bool:
        """Invia dati a NetGescon API"""
        try:
            url = f"{self.config['netgescon']['base_url']}/api/v1/import/{endpoint}"
            
            response = self.session.post(
                url, 
                json=data, 
                timeout=self.config['netgescon']['timeout']
            )
            
            if response.status_code in [200, 201]:
                return True
            elif response.status_code == 409:
                # Record già esistente - aggiorna
                self.logger.debug(f"Record esistente per {endpoint}, tentativo aggiornamento...")
                return self._update_netgescon_record(endpoint, data)
            else:
                self.logger.error(f"Errore API NetGescon {endpoint}: {response.status_code} - {response.text}")
                return False
                
        except Exception as e:
            self.logger.error(f"Errore invio dati a {endpoint}: {e}")
            return False
    
    def _update_netgescon_record(self, endpoint: str, data: Dict[str, Any]) -> bool:
        """Aggiorna record esistente in NetGescon"""
        try:
            # Usa codice_esterno per identificare il record
            codice_esterno = data.get('codice_esterno')
            if not codice_esterno:
                return False
            
            url = f"{self.config['netgescon']['base_url']}/api/v1/import/{endpoint}/{codice_esterno}"
            
            response = self.session.put(
                url, 
                json=data, 
                timeout=self.config['netgescon']['timeout']
            )
            
            return response.status_code in [200, 204]
            
        except Exception as e:
            self.logger.error(f"Errore aggiornamento record {endpoint}: {e}")
            return False
    
    # === MAIN EXECUTION ===
    
    def run_full_import(self) -> bool:
        """Esegue importazione completa"""
        try:
            self.logger.info("=== INIZIO IMPORTAZIONE COMPLETA GESCON → NETGESCON ===")
            
            # Setup
            if not self.connect_gescon():
                return False
            
            self.session = self._setup_http_session()
            
            if not self.test_netgescon_connection():
                return False
            
            # Import sequence
            start_time = datetime.now()
            
            success = (
                self.import_stabili() and
                self.import_unita_immobiliari()
                # TODO: add import_soggetti() and import_movimenti()
            )
            
            duration = datetime.now() - start_time
            
            # Report finale
            self.logger.info("=== REPORT IMPORTAZIONE COMPLETATA ===")
            self.logger.info(f"Durata totale: {duration}")
            self.logger.info(f"Stabili: {self.stats['stabili']['imported']} importati, {self.stats['stabili']['errors']} errori")
            self.logger.info(f"Unità: {self.stats['unita']['imported']} importate, {self.stats['unita']['errors']} errori")
            
            return success
            
        except Exception as e:
            self.logger.error(f"Errore importazione completa: {e}")
            return False
        finally:
            if self.gescon_db:
                self.gescon_db.close()
            if self.session:
                self.session.close()

# === MAIN EXECUTION ===

def main():
    """Entry point principale"""
    import argparse
    
    parser = argparse.ArgumentParser(description='NetGescon Importer v2.0')
    parser.add_argument('--config', '-c', default='config/importer_config.json', 
                       help='File di configurazione')
    parser.add_argument('--mode', '-m', choices=['full', 'incremental', 'test'], 
                       default='incremental', help='Modalità importazione')
    parser.add_argument('--schedule', '-s', action='store_true', 
                       help='Esegue in modalità scheduler')
    
    args = parser.parse_args()
    
    try:
        importer = NetGesconImporter(args.config)
        
        if args.schedule:
            # Modalità scheduler
            schedule.every().day.at("02:00").do(importer.run_full_import)
            schedule.every().hour.do(importer.run_incremental_import)
            
            importer.logger.info("Scheduler avviato. CTRL+C per terminare.")
            
            while True:
                schedule.run_pending()
                time.sleep(60)
        else:
            # Modalità singola esecuzione
            if args.mode == 'full':
                success = importer.run_full_import()
            elif args.mode == 'test':
                success = importer.test_netgescon_connection()
            else:
                success = importer.run_incremental_import()
            
            sys.exit(0 if success else 1)
            
    except KeyboardInterrupt:
        print("\nImportazione interrotta dall'utente")
        sys.exit(0)
    except Exception as e:
        print(f"Errore fatale: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()
