"""
Version mobile de Web Sentinel avec Kivy pour Android.
Interface tactile optimisée pour smartphones et tablettes.
"""

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.uix.textinput import TextInput
from kivy.uix.scrollview import ScrollView
from kivy.uix.popup import Popup
from kivy.uix.progressbar import ProgressBar
from kivy.uix.switch import Switch
from kivy.uix.accordion import Accordion, AccordionItem
from kivy.uix.carousel import Carousel
from kivy.uix.tabbedpanel import TabbedPanel, TabbedPanelItem
from kivy.clock import Clock
from kivy.logger import Logger
from kivy.storage.jsonstore import JsonStore
from kivy.utils import platform

import threading
import json
from typing import List, Dict, Any
from pathlib import Path
import os

# Import des modules Web Sentinel
import sys
sys.path.append(str(Path(__file__).parent.parent.parent))

try:
    from web_sentinel.model import ScanRequest
    from web_sentinel.scanner import SentinelScanner
    from web_sentinel.reporting import HistoryStore, ReportEngine
except ImportError as e:
    Logger.warning(f"Import Web Sentinel: {e}")


class DomainInputWidget(BoxLayout):
    """Widget pour la saisie des domaines."""
    
    def __init__(self, **kwargs):
        super().__init__(orientation='vertical', **kwargs)
        
        # Titre
        title = Label(
            text='🌐 Domaines à scanner',
            size_hint_y=None,
            height=40,
            font_size='18sp',
            bold=True
        )
        self.add_widget(title)
        
        # Zone de texte pour les domaines
        self.domain_input = TextInput(
            hint_text='Entrez vos domaines (un par ligne):\nexemple.com\nmonsite.fr\napi.monapp.com',
            multiline=True,
            font_size='14sp'
        )
        self.add_widget(self.domain_input)
        
        # Boutons d'action
        button_layout = BoxLayout(
            orientation='horizontal',
            size_hint_y=None,
            height=50,
            spacing=10
        )
        
        load_btn = Button(
            text='📁 Charger',
            size_hint_x=0.33
        )
        load_btn.bind(on_press=self.load_domains)
        
        save_btn = Button(
            text='💾 Sauver',
            size_hint_x=0.33
        )
        save_btn.bind(on_press=self.save_domains)
        
        clear_btn = Button(
            text='🗑️ Effacer',
            size_hint_x=0.33
        )
        clear_btn.bind(on_press=self.clear_domains)
        
        button_layout.add_widget(load_btn)
        button_layout.add_widget(save_btn)
        button_layout.add_widget(clear_btn)
        
        self.add_widget(button_layout)
        
    def get_domains(self) -> List[str]:
        """Récupérer la liste des domaines."""
        text = self.domain_input.text.strip()
        if not text:
            return []
        return [line.strip() for line in text.split('\n') if line.strip()]
        
    def set_domains(self, domains: List[str]):
        """Définir la liste des domaines."""
        self.domain_input.text = '\n'.join(domains)
        
    def load_domains(self, instance):
        """Charger les domaines depuis le stockage."""
        # Implémentation simplifiée pour mobile
        pass
        
    def save_domains(self, instance):
        """Sauvegarder les domaines."""
        # Implémentation simplifiée pour mobile
        pass
        
    def clear_domains(self, instance):
        """Effacer les domaines."""
        self.domain_input.text = ''


class ScanOptionsWidget(BoxLayout):
    """Widget pour les options de scan."""
    
    def __init__(self, **kwargs):
        super().__init__(orientation='vertical', **kwargs)
        
        # Titre
        title = Label(
            text='⚙️ Options de scan',
            size_hint_y=None,
            height=40,
            font_size='18sp',
            bold=True
        )
        self.add_widget(title)
        
        # Options dans un accordéon pour économiser l'espace
        self.accordion = Accordion(orientation='vertical')
        
        # Modules
        modules_item = AccordionItem(title='🔧 Modules de scan')
        modules_layout = GridLayout(cols=2, spacing=10, size_hint_y=None)
        modules_layout.bind(minimum_height=modules_layout.setter('height'))
        
        self.module_switches = {}
        modules = [
            ('tls', 'TLS/SSL'),
            ('headers', 'En-têtes HTTP'),
            ('static-analysis', 'Analyse DOM'),
            ('injection', 'Tests d\'injection'),
            ('third-party', 'Outils tiers')
        ]
        
        for module_name, display_name in modules:
            label = Label(text=display_name, size_hint_y=None, height=40)
            switch = Switch(active=True, size_hint_y=None, height=40)
            self.module_switches[module_name] = switch
            
            modules_layout.add_widget(label)
            modules_layout.add_widget(switch)
            
        modules_item.add_widget(modules_layout)
        self.accordion.add_widget(modules_item)
        
        # Options avancées
        advanced_item = AccordionItem(title='🔬 Options avancées')
        advanced_layout = BoxLayout(orientation='vertical', spacing=10)
        
        # Mode invasif
        invasive_layout = BoxLayout(orientation='horizontal', size_hint_y=None, height=40)
        invasive_layout.add_widget(Label(text='Mode invasif:'))
        self.invasive_switch = Switch(active=False)
        invasive_layout.add_widget(self.invasive_switch)
        advanced_layout.add_widget(invasive_layout)
        
        # Timeout
        timeout_layout = BoxLayout(orientation='horizontal', size_hint_y=None, height=40)
        timeout_layout.add_widget(Label(text='Timeout (sec):'))
        self.timeout_input = TextInput(
            text='5.0',
            input_filter='float',
            multiline=False,
            size_hint_x=0.3
        )
        timeout_layout.add_widget(self.timeout_input)
        advanced_layout.add_widget(timeout_layout)
        
        advanced_item.add_widget(advanced_layout)
        self.accordion.add_widget(advanced_item)
        
        self.add_widget(self.accordion)
        
    def get_selected_modules(self) -> List[str]:
        """Récupérer les modules sélectionnés."""
        return [name for name, switch in self.module_switches.items() if switch.active]
        
    def get_timeout(self) -> float:
        """Récupérer la valeur du timeout."""
        try:
            return float(self.timeout_input.text)
        except ValueError:
            return 5.0
            
    def is_invasive_enabled(self) -> bool:
        """Vérifier si le mode invasif est activé."""
        return self.invasive_switch.active


class ResultsWidget(BoxLayout):
    """Widget pour afficher les résultats."""
    
    def __init__(self, **kwargs):
        super().__init__(orientation='vertical', **kwargs)
        
        # Titre avec statistiques
        self.stats_label = Label(
            text='📊 Résultats du scan',
            size_hint_y=None,
            height=40,
            font_size='18sp',
            bold=True
        )
        self.add_widget(self.stats_label)
        
        # Panneau à onglets pour organiser les résultats
        self.tab_panel = TabbedPanel(do_default_tab=False)
        
        # Onglet Résumé
        summary_tab = TabbedPanelItem(text='📊 Résumé')
        self.summary_content = Label(
            text='Aucun scan effectué',
            text_size=(None, None),
            valign='top'
        )
        summary_tab.add_widget(self.summary_content)
        self.tab_panel.add_widget(summary_tab)
        
        # Onglet Détails
        details_tab = TabbedPanelItem(text='🔍 Détails')
        
        self.details_scroll = ScrollView()
        self.details_layout = BoxLayout(
            orientation='vertical',
            spacing=10,
            size_hint_y=None
        )
        self.details_layout.bind(minimum_height=self.details_layout.setter('height'))
        self.details_scroll.add_widget(self.details_layout)
        details_tab.add_widget(self.details_scroll)
        
        self.tab_panel.add_widget(details_tab)
        
        # Onglet Actions
        actions_tab = TabbedPanelItem(text='💡 Actions')
        actions_layout = BoxLayout(orientation='vertical', spacing=10, padding=20)
        
        export_btn = Button(
            text='📄 Exporter rapport HTML',
            size_hint_y=None,
            height=50
        )
        export_btn.bind(on_press=self.export_html_report)
        
        share_btn = Button(
            text='📱 Partager résultats',
            size_hint_y=None,
            height=50
        )
        share_btn.bind(on_press=self.share_results)
        
        actions_layout.add_widget(export_btn)
        actions_layout.add_widget(share_btn)
        actions_tab.add_widget(actions_layout)
        
        self.tab_panel.add_widget(actions_tab)
        
        self.add_widget(self.tab_panel)
        
        # Stockage des résultats
        self.scan_results = []
        
    def update_results(self, results: List[Any]):
        """Mettre à jour l'affichage des résultats."""
        self.scan_results = results
        
        # Calculer les statistiques
        total_findings = sum(len(result.findings) for result in results)
        total_domains = len(results)
        
        severity_counts = {'critical': 0, 'high': 0, 'medium': 0, 'low': 0, 'info': 0}
        for result in results:
            for finding in result.findings:
                severity_counts[finding.severity] += 1
                
        # Mettre à jour le résumé
        self.stats_label.text = f'📊 Résultats: {total_domains} domaines, {total_findings} observations'
        
        summary_text = f"""📊 RÉSUMÉ DU SCAN

🌐 Domaines scannés: {total_domains}
🔍 Observations totales: {total_findings}

📈 RÉPARTITION PAR SÉVÉRITÉ:
🔴 Critique: {severity_counts['critical']}
🟠 Élevé: {severity_counts['high']}
🟡 Moyen: {severity_counts['medium']}
🔵 Faible: {severity_counts['low']}
ℹ️ Info: {severity_counts['info']}

🎯 SCORE DE SÉCURITÉ:
{self._calculate_security_score(severity_counts)}/100
"""
        
        self.summary_content.text = summary_text
        self.summary_content.text_size = (self.summary_content.width, None)
        
        # Mettre à jour les détails
        self.details_layout.clear_widgets()
        
        for result in results:
            domain_widget = self._create_domain_result_widget(result)
            self.details_layout.add_widget(domain_widget)
            
    def _calculate_security_score(self, severity_counts: Dict[str, int]) -> int:
        """Calculer un score de sécurité basé sur les observations."""
        total = sum(severity_counts.values())
        if total == 0:
            return 100
            
        # Pondération des sévérités
        weights = {'critical': -25, 'high': -15, 'medium': -8, 'low': -3, 'info': -1}
        penalty = sum(count * weights[severity] for severity, count in severity_counts.items())
        
        score = max(0, min(100, 100 + penalty))
        return score
        
    def _create_domain_result_widget(self, result) -> BoxLayout:
        """Créer un widget pour afficher les résultats d'un domaine."""
        domain_layout = BoxLayout(
            orientation='vertical',
            spacing=5,
            size_hint_y=None,
            height=80 + (len(result.findings) * 60)
        )
        
        # En-tête du domaine
        domain_header = BoxLayout(
            orientation='horizontal',
            size_hint_y=None,
            height=50
        )
        
        domain_label = Label(
            text=f'🌐 {result.request.domain}',
            font_size='16sp',
            bold=True,
            size_hint_x=0.7
        )
        
        findings_count = Label(
            text=f'{len(result.findings)} obs.',
            font_size='14sp',
            size_hint_x=0.3
        )
        
        domain_header.add_widget(domain_label)
        domain_header.add_widget(findings_count)
        domain_layout.add_widget(domain_header)
        
        # Liste des observations
        for finding in result.findings[:10]:  # Limiter pour l'affichage mobile
            finding_widget = self._create_finding_widget(finding)
            domain_layout.add_widget(finding_widget)
            
        if len(result.findings) > 10:
            more_label = Label(
                text=f'... et {len(result.findings) - 10} autres observations',
                size_hint_y=None,
                height=30,
                font_size='12sp',
                color=(0.6, 0.6, 0.6, 1)
            )
            domain_layout.add_widget(more_label)
            
        return domain_layout
        
    def _create_finding_widget(self, finding) -> BoxLayout:
        """Créer un widget pour une observation."""
        finding_layout = BoxLayout(
            orientation='horizontal',
            size_hint_y=None,
            height=50,
            spacing=10
        )
        
        # Indicateur de sévérité (couleur)
        severity_colors = {
            'critical': (0.8, 0.1, 0.1, 1),
            'high': (1, 0.3, 0.1, 1),
            'medium': (1, 0.7, 0.1, 1),
            'low': (0.1, 0.6, 1, 1),
            'info': (0.1, 0.8, 0.5, 1)
        }
        
        severity_indicator = Label(
            text='●',
            font_size='20sp',
            color=severity_colors.get(finding.severity, (0.5, 0.5, 0.5, 1)),
            size_hint_x=None,
            width=30
        )
        
        # Titre de l'observation
        title_label = Label(
            text=f'[{finding.check}] {finding.title}',
            font_size='13sp',
            text_size=(None, None),
            halign='left',
            valign='center'
        )
        
        # Bouton détails
        details_btn = Button(
            text='ℹ️',
            size_hint_x=None,
            width=40,
            font_size='16sp'
        )
        details_btn.bind(on_press=lambda x: self.show_finding_details(finding))
        
        finding_layout.add_widget(severity_indicator)
        finding_layout.add_widget(title_label)
        finding_layout.add_widget(details_btn)
        
        return finding_layout
        
    def show_finding_details(self, finding):
        """Afficher les détails d'une observation."""
        content = BoxLayout(orientation='vertical', spacing=10, padding=20)
        
        # Titre
        title = Label(
            text=finding.title,
            font_size='16sp',
            bold=True,
            size_hint_y=None,
            height=40
        )
        content.add_widget(title)
        
        # Description scrollable
        desc_scroll = ScrollView()
        desc_label = Label(
            text=f"📝 Description:\n{finding.description}\n\n💡 Recommandation:\n{finding.remediation}",
            text_size=(400, None),
            halign='left',
            valign='top'
        )
        desc_scroll.add_widget(desc_label)
        content.add_widget(desc_scroll)
        
        # Bouton fermer
        close_btn = Button(
            text='Fermer',
            size_hint_y=None,
            height=50
        )
        
        popup = Popup(
            title=f'Détails - {finding.check}',
            content=content,
            size_hint=(0.9, 0.7)
        )
        
        close_btn.bind(on_press=popup.dismiss)
        content.add_widget(close_btn)
        
        popup.open()
        
    def export_html_report(self, instance):
        """Exporter le rapport HTML."""
        if not self.scan_results:
            popup = Popup(
                title='Attention',
                content=Label(text='Aucun résultat à exporter'),
                size_hint=(0.8, 0.3)
            )
            popup.open()
            return
            
        # Sur Android, sauvegarder dans le stockage externe
        if platform == 'android':
            from android.storage import primary_external_storage_path
            reports_dir = Path(primary_external_storage_path()) / "WebSentinel" / "reports"
        else:
            reports_dir = Path.home() / "WebSentinel" / "reports"
            
        reports_dir.mkdir(parents=True, exist_ok=True)
        
        # Générer le rapport
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        report_file = reports_dir / f"web_sentinel_report_{timestamp}.html"
        
        try:
            history_store = HistoryStore(reports_dir / "history.json")
            report_engine = ReportEngine(history_store=history_store)
            
            # Utiliser le premier résultat comme base
            context = report_engine.build_context(self.scan_results[0])
            html_content = report_engine.render_html(context)
            
            with open(report_file, 'w', encoding='utf-8') as f:
                f.write(html_content)
                
            popup = Popup(
                title='Succès',
                content=Label(text=f'Rapport sauvegardé:\n{report_file}'),
                size_hint=(0.8, 0.4)
            )
            popup.open()
            
        except Exception as e:
            popup = Popup(
                title='Erreur',
                content=Label(text=f'Erreur lors de l\'export:\n{str(e)}'),
                size_hint=(0.8, 0.4)
            )
            popup.open()
            
    def share_results(self, instance):
        """Partager les résultats."""
        if not self.scan_results:
            return
            
        # Créer un résumé textuel
        total_findings = sum(len(result.findings) for result in self.scan_results)
        summary = f"🛡️ Web Sentinel - Rapport de sécurité\n\n"
        summary += f"📊 {len(self.scan_results)} domaines scannés\n"
        summary += f"🔍 {total_findings} observations détectées\n\n"
        
        # Sur Android, utiliser l'intent de partage
        if platform == 'android':
            from jnius import autoclass
            
            PythonActivity = autoclass('org.kivy.android.PythonActivity')
            Intent = autoclass('android.content.Intent')
            String = autoclass('java.lang.String')
            
            intent = Intent()
            intent.setAction(Intent.ACTION_SEND)
            intent.putExtra(Intent.EXTRA_TEXT, String(summary))
            intent.setType("text/plain")
            
            currentActivity = PythonActivity.mActivity
            currentActivity.startActivity(intent)
        else:
            # Sur desktop, copier dans le presse-papier
            popup = Popup(
                title='Partage',
                content=Label(text='Fonctionnalité de partage disponible sur Android'),
                size_hint=(0.8, 0.3)
            )
            popup.open()


class WebSentinelMobileApp(App):
    """Application mobile Web Sentinel."""
    
    def build(self):
        """Construire l'interface de l'application."""
        self.title = 'Web Sentinel Mobile'
        
        # Layout principal
        main_layout = BoxLayout(orientation='vertical', spacing=10, padding=10)
        
        # En-tête
        header = Label(
            text='🛡️ Web Sentinel Mobile',
            font_size='24sp',
            bold=True,
            size_hint_y=None,
            height=60
        )
        main_layout.add_widget(header)
        
        # Carrousel pour naviguer entre les sections
        self.carousel = Carousel(direction='right')
        
        # Page 1: Configuration des domaines
        domains_page = BoxLayout(orientation='vertical', padding=20)
        self.domain_widget = DomainInputWidget()
        domains_page.add_widget(self.domain_widget)
        self.carousel.add_widget(domains_page)
        
        # Page 2: Options de scan
        options_page = BoxLayout(orientation='vertical', padding=20)
        self.options_widget = ScanOptionsWidget()
        options_page.add_widget(self.options_widget)
        self.carousel.add_widget(options_page)
        
        # Page 3: Résultats
        results_page = BoxLayout(orientation='vertical', padding=20)
        self.results_widget = ResultsWidget()
        results_page.add_widget(self.results_widget)
        self.carousel.add_widget(results_page)
        
        main_layout.add_widget(self.carousel)
        
        # Barre de navigation
        nav_layout = BoxLayout(
            orientation='horizontal',
            size_hint_y=None,
            height=60,
            spacing=5
        )
        
        domains_btn = Button(text='🌐 Domaines')
        domains_btn.bind(on_press=lambda x: setattr(self.carousel, 'index', 0))
        
        options_btn = Button(text='⚙️ Options')
        options_btn.bind(on_press=lambda x: setattr(self.carousel, 'index', 1))
        
        self.scan_btn = Button(text='🚀 Scanner')
        self.scan_btn.bind(on_press=self.start_scan)
        
        results_btn = Button(text='📊 Résultats')
        results_btn.bind(on_press=lambda x: setattr(self.carousel, 'index', 2))
        
        nav_layout.add_widget(domains_btn)
        nav_layout.add_widget(options_btn)
        nav_layout.add_widget(self.scan_btn)
        nav_layout.add_widget(results_btn)
        
        main_layout.add_widget(nav_layout)
        
        # Barre de progression (cachée par défaut)
        self.progress_bar = ProgressBar(
            max=100,
            value=0,
            size_hint_y=None,
            height=0
        )
        main_layout.add_widget(self.progress_bar)
        
        # Stockage local
        data_dir = self.user_data_dir
        Path(data_dir).mkdir(parents=True, exist_ok=True)
        self.store = JsonStore(os.path.join(data_dir, 'config.json'))
        
        return main_layout
        
    def start_scan(self, instance):
        """Démarrer le scan."""
        domains = self.domain_widget.get_domains()
        if not domains:
            popup = Popup(
                title='Attention',
                content=Label(text='Veuillez entrer au moins un domaine'),
                size_hint=(0.8, 0.3)
            )
            popup.open()
            return
            
        selected_modules = self.options_widget.get_selected_modules()
        if not selected_modules:
            popup = Popup(
                title='Attention',
                content=Label(text='Veuillez sélectionner au moins un module'),
                size_hint=(0.8, 0.3)
            )
            popup.open()
            return
            
        # Désactiver le bouton et afficher la progression
        self.scan_btn.text = '⏳ Scan en cours...'
        self.scan_btn.disabled = True
        self.progress_bar.height = 20
        self.progress_bar.value = 0
        
        # Lancer le scan en thread séparé
        scan_thread = threading.Thread(
            target=self.run_scan_thread,
            args=(domains, selected_modules),
            daemon=True
        )
        scan_thread.start()
        
    def run_scan_thread(self, domains: List[str], modules: List[str]):
        """Exécuter le scan dans un thread séparé."""
        try:
            scanner = SentinelScanner()
            results = []
            total_domains = len(domains)
            
            for i, domain in enumerate(domains):
                # Mettre à jour la progression
                progress = int((i / total_domains) * 100)
                Clock.schedule_once(lambda dt: setattr(self.progress_bar, 'value', progress))
                
                request = ScanRequest(
                    domain=domain,
                    timeout=self.options_widget.get_timeout(),
                    allow_invasive=self.options_widget.is_invasive_enabled()
                )
                
                try:
                    result = scanner.run(request, enabled_modules=modules)
                    results.append(result)
                except Exception as e:
                    Logger.error(f"Erreur scan {domain}: {e}")
                    
            # Scan terminé
            Clock.schedule_once(lambda dt: self.scan_completed(results))
            
        except Exception as e:
            Clock.schedule_once(lambda dt: self.scan_error(str(e)))
            
    def scan_completed(self, results):
        """Callback quand le scan est terminé."""
        self.scan_btn.text = '🚀 Scanner'
        self.scan_btn.disabled = False
        self.progress_bar.height = 0
        self.progress_bar.value = 100
        
        # Mettre à jour les résultats
        self.results_widget.update_results(results)
        
        # Naviguer vers les résultats
        self.carousel.index = 2
        
        # Notification de succès
        popup = Popup(
            title='Scan terminé',
            content=Label(text=f'Scan de {len(results)} domaines terminé avec succès!'),
            size_hint=(0.8, 0.3)
        )
        popup.open()
        
    def scan_error(self, error: str):
        """Callback en cas d'erreur de scan."""
        self.scan_btn.text = '🚀 Scanner'
        self.scan_btn.disabled = False
        self.progress_bar.height = 0
        
        popup = Popup(
            title='Erreur',
            content=Label(text=f'Erreur lors du scan:\n{error}'),
            size_hint=(0.8, 0.4)
        )
        popup.open()


def main():
    """Point d'entrée de l'application mobile."""
    WebSentinelMobileApp().run()


if __name__ == '__main__':
    main()