"""
CE365 Agent - AI-powered IT Maintenance Assistant

Copyright (c) 2026 Carsten Eckhardt / Eckhardt-Marketing
Licensed under Source Available License
"""

import asyncio
from typing import List, Dict, Any, Optional
from datetime import datetime
from ce365.core.providers import create_provider
from ce365.core.commands import SlashCommandHandler
from ce365.core.session import Session
from ce365.tools.registry import ToolRegistry
from ce365.tools.executor import CommandExecutor
from ce365.workflow.state_machine import WorkflowStateMachine
from ce365.workflow.lock import ExecutionLock
from ce365.storage.changelog import ChangelogWriter
from ce365.config.system_prompt import get_system_prompt
from ce365.ui.console import RichConsole

# Learning System
from ce365.learning.case_library import CaseLibrary, Case

# Security - PII Detection
try:
    from ce365.security.pii_detector import get_pii_detector
    PII_AVAILABLE = True
except ImportError:
    PII_AVAILABLE = False

# Tools importieren
from ce365.tools.audit.system_info import SystemInfoTool
from ce365.tools.audit.logs import CheckSystemLogsTool
from ce365.tools.audit.processes import CheckRunningProcessesTool
from ce365.tools.audit.updates import CheckSystemUpdatesTool
from ce365.tools.audit.backup import CheckBackupStatusTool, ListBackupsTool, VerifyBackupTool
from ce365.tools.audit.stress_tests import (
    StressTestCPUTool, StressTestMemoryTool, TestDiskSpeedTool,
    CheckSystemTemperatureTool, RunStabilityTestTool
)
from ce365.tools.audit.reporting import GenerateSystemReportTool
from ce365.tools.audit.pdf_report import SaveReportPDFTool
from ce365.tools.audit.security import CheckSecurityStatusTool
from ce365.tools.audit.startup import CheckStartupProgramsTool
from ce365.tools.audit.malware_scan import MalwareScanTool
from ce365.tools.audit.drivers import CheckDriversTool
from ce365.tools.audit.network_diagnostics import NetworkDiagnosticsTool
from ce365.tools.audit.software_inventory import SoftwareInventoryTool
from ce365.tools.audit.disk_health import DiskHealthTool
from ce365.tools.audit.network_security import NetworkSecurityAuditTool
from ce365.tools.audit.wifi_info import WiFiInfoTool
from ce365.tools.audit.user_accounts import UserAccountAuditTool
from ce365.tools.audit.hosts_file import HostsFileViewerTool
from ce365.tools.audit.scheduled_tasks import ScheduledTasksAuditTool
from ce365.tools.audit.encryption_status import EncryptionStatusTool
from ce365.tools.audit.printer_status import PrinterStatusTool
from ce365.tools.audit.battery_health import BatteryHealthTool
from ce365.tools.repair.service_manager import ServiceManagerTool
from ce365.tools.repair.disk_cleanup import DiskCleanupTool
from ce365.tools.repair.network_tools import FlushDNSCacheTool, ResetNetworkStackTool
from ce365.tools.repair.system_repair import (
    RunSFCScanTool, RunDISMRepairTool, RunChkdskTool,
    RepairDiskPermissionsTool, RepairDiskTool,
)
from ce365.tools.repair.updates import InstallSystemUpdatesTool
from ce365.tools.repair.backup import (
    CreateRestorePointTool, TriggerTimeMachineBackupTool,
    StopBackupTool, ManageBackupExclusionsTool, ManageSnapshotsTool
)
from ce365.tools.repair.startup import DisableStartupProgramTool, EnableStartupProgramTool
from ce365.tools.repair.update_scheduler import ScheduleSystemUpdatesTool
from ce365.tools.repair.process_manager import KillProcessTool
from ce365.tools.repair.browser_cleanup import BrowserCacheCleanupTool
from ce365.tools.repair.disk_optimize import OptimizeDriveTool
from ce365.tools.repair.windows_update_reset import ResetWindowsUpdateTool
from ce365.tools.repair.cache_rebuild import RebuildCacheTool
from ce365.tools.repair.task_scheduler import ManageScheduledTaskTool
from ce365.tools.repair.power_management import RebootTool, CancelShutdownTool
from ce365.tools.audit.directory_listing import ListDirectoryTool
from ce365.tools.repair.batch_file_ops import BatchMoveFilesTool
from ce365.tools.repair.software_manager import (
    InstallSoftwareTool, UninstallSoftwareTool, UpdateSoftwareTool,
)
from ce365.tools.repair.user_manager import (
    CreateUserTool, DeleteUserTool, ChangePasswordTool, ToggleUserTool,
)
from ce365.tools.repair.file_manager import (
    WriteFileTool, EditFileTool, DeleteFileTool, MoveFileTool,
)
from ce365.tools.research.web_search import WebSearchTool, WebSearchInstantAnswerTool
from ce365.tools.analysis.root_cause import RootCauseAnalyzer  # NEU
from ce365.tools.analysis.consult_specialist import ConsultSpecialistTool  # Multi-Agent
from ce365.tools.audit.incident_report import IncidentReportTool  # SOAP Incident Report
from ce365.tools.audit.memory_dump import AnalyzeMemoryDumpsTool  # Memory Dump Analyse
from ce365.tools.audit.sip_gatekeeper import SIPGatekeeperAuditTool  # SIP & Gatekeeper Deep Audit
from ce365.tools.audit.tcc_privacy import TCCPrivacyAuditTool  # TCC Privacy Audit
from ce365.tools.audit.mdm_enrollment import MDMEnrollmentAuditTool  # MDM & Enrollment Audit
from ce365.tools.audit.apple_intelligence import AppleIntelligenceAuditTool  # Apple Intelligence & Shortcuts
from ce365.core.agents import SpecialistAgent, SPECIALISTS  # Multi-Agent System

# License & Usage
from ce365.core.license import validate_license, check_edition_features
from ce365.core.usage_tracker import UsageTracker
from ce365.config.settings import get_settings

# Hook System
from ce365.workflow.hooks import HookManager, HookEvent, HookContext

# SSH + WinRM Remote
from ce365.core.ssh import SSHConnectionManager
from ce365.core.winrm import WinRMConnectionManager

# MCP Integration
from ce365.integrations.mcp_manager import MCPManager


class CE365Bot:
    """
    CE365 Agent - IT-Wartungs-Assistent

    Orchestriert:
    - Anthropic Tool Use Loop
    - Workflow State Machine
    - Tool Execution
    - User Interaction
    """

    def __init__(self):
        # Core Components — Multi-Provider
        settings = get_settings()
        provider_keys = {
            "anthropic": settings.anthropic_api_key,
            "openai": settings.openai_api_key,
            "openrouter": settings.openrouter_api_key,
        }
        self.client = create_provider(
            provider_name=settings.llm_provider,
            api_key=provider_keys[settings.llm_provider],
            model=settings.llm_model,
        )
        self.session = Session()
        self.console = RichConsole()
        self.state_machine = WorkflowStateMachine()
        self.changelog = ChangelogWriter(self.session.session_id)

        # Learning System
        self.case_library = CaseLibrary()

        # Security - PII Detection
        if PII_AVAILABLE:
            self.pii_detector = get_pii_detector()
        else:
            self.pii_detector = None

        # Session Tracking für Learning
        self.session_start_time = datetime.now()
        self.detected_os_type: Optional[str] = None
        self.detected_os_version: Optional[str] = None
        self.problem_description: Optional[str] = None
        self.error_codes: Optional[str] = None
        self.diagnosed_root_cause: Optional[str] = None
        self.similar_case_offered: Optional[int] = None  # Case ID wenn angeboten
        self.customer_name: Optional[str] = None
        self.ticket_id: Optional[str] = None

        # Edition-Validierung VOR Tool-Registrierung:
        # Nur "free", "core" und "scale" sind gültige Editionen
        if settings.edition not in ("free", "core", "scale"):
            settings.edition = "free"

        # Core/Scale ohne gültige Credentials → Downgrade auf Free
        if settings.edition in ("core", "scale") and (not settings.license_key or not settings.license_server_url):
            settings.edition = "free"
            self._edition_downgraded = True
        else:
            self._edition_downgraded = False

        # Usage Tracker (Free: 1 Repair total, 5 Sessions/Monat)
        self.usage_tracker = UsageTracker(edition=settings.edition)

        # Session Management (Pro: Heartbeat)
        self._session_token: Optional[str] = None
        self._heartbeat_task: Optional[asyncio.Task] = None

        # Slash Commands
        self.command_handler = SlashCommandHandler()

        # Hook System (Pro)
        self.hook_manager = HookManager()
        if check_edition_features(settings.edition, "hooks"):
            self._register_hooks()

        # SSH + WinRM Remote (Pro)
        self.ssh_manager: Optional[SSHConnectionManager] = None
        self.winrm_manager: Optional[WinRMConnectionManager] = None
        if check_edition_features(settings.edition, "ssh_remote"):
            self.ssh_manager = SSHConnectionManager()
            self.winrm_manager = WinRMConnectionManager()

        # MCP Integration (Pro)
        self.mcp_manager: Optional[MCPManager] = None
        if check_edition_features(settings.edition, "mcp_integration"):
            self.mcp_manager = MCPManager()

        # Tool System
        self.tool_registry = ToolRegistry()
        self._register_tools()

        # Executor (mit Hook-System)
        self.executor = CommandExecutor(
            tool_registry=self.tool_registry,
            state_machine=self.state_machine,
            changelog_writer=self.changelog,
            usage_tracker=self.usage_tracker,
            hook_manager=self.hook_manager,
        )

        # System Prompt
        self.system_prompt = get_system_prompt()

    def _register_hooks(self):
        """Eingebaute Hooks registrieren (Pro Edition)"""
        from ce365.workflow.builtin_hooks import get_builtin_hooks
        for hook in get_builtin_hooks():
            self.hook_manager.register(hook)

    def _register_tools(self):
        """Alle Tools registrieren (mit Edition-basiertem Feature-Gating)"""
        settings = get_settings()
        edition = settings.edition

        # === Basis-Audit Tools (alle Editionen) ===
        self.tool_registry.register(SystemInfoTool())
        self.tool_registry.register(CheckSystemLogsTool())
        self.tool_registry.register(CheckRunningProcessesTool())
        self.tool_registry.register(CheckSystemUpdatesTool())
        self.tool_registry.register(CheckBackupStatusTool())
        self.tool_registry.register(CheckSecurityStatusTool())
        self.tool_registry.register(CheckStartupProgramsTool())
        self.tool_registry.register(NetworkDiagnosticsTool())
        self.tool_registry.register(WiFiInfoTool())
        self.tool_registry.register(PrinterStatusTool())
        self.tool_registry.register(ListDirectoryTool())

        # === Basis-Repair Tools (alle Editionen, Free: 5/Monat) ===
        self.tool_registry.register(ServiceManagerTool())
        self.tool_registry.register(DiskCleanupTool())
        self.tool_registry.register(FlushDNSCacheTool())
        self.tool_registry.register(KillProcessTool())

        # === Erweiterte Audit Tools (Pro) ===
        if check_edition_features(edition, "advanced_audit"):
            self.tool_registry.register(StressTestCPUTool())
            self.tool_registry.register(StressTestMemoryTool())
            self.tool_registry.register(TestDiskSpeedTool())
            self.tool_registry.register(CheckSystemTemperatureTool())
            self.tool_registry.register(RunStabilityTestTool())
            self.tool_registry.register(MalwareScanTool())
            self.tool_registry.register(GenerateSystemReportTool())
            self.tool_registry.register(CheckDriversTool())
            self.tool_registry.register(SaveReportPDFTool())
            self.tool_registry.register(SoftwareInventoryTool())
            self.tool_registry.register(DiskHealthTool())
            self.tool_registry.register(NetworkSecurityAuditTool())
            self.tool_registry.register(UserAccountAuditTool())
            self.tool_registry.register(HostsFileViewerTool())
            self.tool_registry.register(ScheduledTasksAuditTool())
            self.tool_registry.register(EncryptionStatusTool())
            self.tool_registry.register(BatteryHealthTool())
            self.tool_registry.register(ListBackupsTool())
            self.tool_registry.register(VerifyBackupTool())
            self.tool_registry.register(AnalyzeMemoryDumpsTool())
            self.tool_registry.register(SIPGatekeeperAuditTool())
            self.tool_registry.register(TCCPrivacyAuditTool())
            self.tool_registry.register(MDMEnrollmentAuditTool())
            self.tool_registry.register(AppleIntelligenceAuditTool())

        # === Erweiterte Repair Tools (Pro) ===
        if check_edition_features(edition, "advanced_repair"):
            self.tool_registry.register(RunSFCScanTool())
            self.tool_registry.register(RunDISMRepairTool())
            self.tool_registry.register(RunChkdskTool())
            self.tool_registry.register(RepairDiskPermissionsTool())
            self.tool_registry.register(RepairDiskTool())
            self.tool_registry.register(ResetNetworkStackTool())
            self.tool_registry.register(InstallSystemUpdatesTool())
            self.tool_registry.register(CreateRestorePointTool())
            self.tool_registry.register(TriggerTimeMachineBackupTool())
            self.tool_registry.register(StopBackupTool())
            self.tool_registry.register(ManageBackupExclusionsTool())
            self.tool_registry.register(ManageSnapshotsTool())
            self.tool_registry.register(DisableStartupProgramTool())
            self.tool_registry.register(EnableStartupProgramTool())
            self.tool_registry.register(ScheduleSystemUpdatesTool())
            self.tool_registry.register(BrowserCacheCleanupTool())
            self.tool_registry.register(OptimizeDriveTool())
            self.tool_registry.register(ResetWindowsUpdateTool())
            self.tool_registry.register(RebuildCacheTool())
            self.tool_registry.register(ManageScheduledTaskTool())

        # === Steuerungs-Tools (Pro) ===
        if check_edition_features(edition, "system_control"):
            self.tool_registry.register(RebootTool())
            self.tool_registry.register(CancelShutdownTool())
            self.tool_registry.register(InstallSoftwareTool())
            self.tool_registry.register(UninstallSoftwareTool())
            self.tool_registry.register(UpdateSoftwareTool())
            self.tool_registry.register(CreateUserTool())
            self.tool_registry.register(DeleteUserTool())
            self.tool_registry.register(ChangePasswordTool())
            self.tool_registry.register(ToggleUserTool())
            self.tool_registry.register(WriteFileTool())
            self.tool_registry.register(EditFileTool())
            self.tool_registry.register(DeleteFileTool())
            self.tool_registry.register(MoveFileTool())
            self.tool_registry.register(BatchMoveFilesTool())

        # === File Reading Tools (Pro) ===
        if check_edition_features(edition, "file_reading"):
            from ce365.tools.audit.file_reader import (
                ReadFileTool, SearchInFileTool, TailLogTool,
            )
            self.tool_registry.register(ReadFileTool())
            self.tool_registry.register(SearchInFileTool())
            self.tool_registry.register(TailLogTool())

        # === Web Search + Root Cause Analysis (Pro) ===
        if check_edition_features(edition, "web_search"):
            self.tool_registry.register(WebSearchTool())
            self.tool_registry.register(WebSearchInstantAnswerTool())

        if check_edition_features(edition, "root_cause_analysis"):
            self.tool_registry.register(RootCauseAnalyzer())

        # === Multi-Agent: Spezialist konsultieren (alle Editionen) ===
        self.tool_registry.register(ConsultSpecialistTool())

        # === Incident Report (alle Editionen) ===
        self.tool_registry.register(IncidentReportTool(
            session=self.session,
            changelog=self.changelog,
            state_machine=self.state_machine,
            bot=self,
        ))

        # Tool-Count wird im Startup-Panel angezeigt
        self._tool_count = len(self.tool_registry)
        self._audit_tool_count = len(self.tool_registry.get_audit_tools())
        self._repair_tool_count = len(self.tool_registry.get_repair_tools())

    async def run(self):
        """Main Bot Loop"""
        self.console.display_logo()

        # Lizenz-Check (wenn License Key gesetzt ist)
        await self._check_license()

        # Update-Check (passiv, 1x pro Tag)
        self._check_for_updates()

        # Core/Scale: Session starten + Heartbeat
        settings = get_settings()
        if settings.edition in ("core", "scale") and settings.license_server_url:
            await self._start_session()

        # Free: Session-Counter prüfen
        if settings.edition == "free":
            if not self.usage_tracker.can_start_session():
                self.console.display_error(self.usage_tracker.get_session_limit_message())
                return
            self.usage_tracker.increment_session()

        # ToS Akzeptanz prüfen (nur beim ersten Start)
        if not self._check_tos_acceptance():
            return

        # Konsolidiertes Startup-Panel
        await self._display_startup_panel()

        # MCP Auto-Connect (Pro)
        if self.mcp_manager:
            try:
                auto_results = await self.mcp_manager.auto_connect()
                for server_name, tools in auto_results.items():
                    for tool in tools:
                        try:
                            self.tool_registry.register(tool)
                        except ValueError:
                            pass
                    if tools:
                        self.console.display_info(
                            f"MCP '{server_name}' auto-connected ({len(tools)} Tools)"
                        )
            except Exception:
                pass  # Auto-Connect darf Start nicht blockieren

        while True:
            try:
                # User Input
                user_input = self.console.get_input()

                if not user_input:
                    continue

                # Exit Commands
                if user_input.lower() in ["exit", "quit", "q"]:
                    self.console.display_success("Session beendet. Auf Wiedersehen!")
                    break

                # Slash Commands + Aliases
                if self.command_handler.is_command(user_input):
                    await self.command_handler.execute(user_input, self)
                    continue

                # GO REPAIR Command
                if ExecutionLock.is_go_command(user_input):
                    await self.handle_go_repair(user_input)
                    continue

                # Learning: Problem-Info extrahieren
                self._extract_problem_info(user_input)

                # Learning: Nach 2+ Messages, prüfe ob ähnliche Fälle
                if len(self.session.messages) >= 2 and self.detected_os_type:
                    # Nur einmal anbieten
                    if self.similar_case_offered is None:
                        similar_found = await self._check_for_similar_cases()
                        if similar_found:
                            # Warte auf User-Entscheidung (1 oder 2)
                            continue

                # Process Message
                await self.process_message(user_input)

            except KeyboardInterrupt:
                self.console.display_warning("\n\nSession unterbrochen. Beende...")
                break
            except Exception as e:
                self.console.display_error(f"Unerwarteter Fehler: {str(e)}")

        # Session-End Hooks
        if self.hook_manager.enabled:
            end_context = HookContext(
                event=HookEvent.SESSION_END,
                session_id=self.session.session_id,
            )
            end_result = await self.hook_manager.run_hooks(HookEvent.SESSION_END, end_context)
            if end_result.message:
                self.console.display_info(end_result.message)

        # Incident Report anbieten (wenn Changelog-Eintraege vorhanden)
        if self.changelog.entries:
            await self._offer_incident_report()

        # MCP-Server trennen
        if self.mcp_manager:
            await self.mcp_manager.disconnect_all()

        # Remote-Verbindungen trennen (SSH / WinRM)
        from ce365.core.command_runner import get_command_runner
        runner = get_command_runner()
        if self.ssh_manager and self.ssh_manager.is_connected:
            await self.ssh_manager.disconnect()
        if self.winrm_manager and self.winrm_manager.is_connected:
            await self.winrm_manager.disconnect()
        if runner.is_remote:
            runner.set_local()

        # Session freigeben (Pro)
        await self._release_session()

        # Cleanup
        if self.changelog.entries:
            self.console.display_info("\n📝 Changelog gespeichert:")
            self.console.display_info(str(self.changelog.log_path))

            # Learning: Session speichern wenn erfolgreich
            if self.state_machine.current_state.value == "completed":
                await self._save_session_as_case(success=True)

    async def _offer_incident_report(self):
        """Bietet am Session-Ende einen Incident Report an"""
        try:
            self.console.display_separator()
            choice = self.console.get_input(
                "Incident Report erstellen? [M]arkdown / [S]OAP / [N]ein"
            ).strip().lower()

            if choice in ("m", "markdown"):
                report_tool = self.tool_registry.get_tool("generate_incident_report")
                if report_tool:
                    report = await report_tool.execute(format="markdown", include_audit_log=True)
                    self.console.display_separator()
                    self.console.console.print(report)
            elif choice in ("s", "soap"):
                report_tool = self.tool_registry.get_tool("generate_incident_report")
                if report_tool:
                    report = await report_tool.execute(format="soap", include_audit_log=True)
                    self.console.display_separator()
                    self.console.console.print(report)
            else:
                pass  # Kein Report gewuenscht
        except (KeyboardInterrupt, EOFError):
            pass  # User hat abgebrochen

    def _check_tos_acceptance(self) -> bool:
        """
        Prüft ob User ToS akzeptiert hat

        Returns:
            True wenn akzeptiert, False wenn abgelehnt (Bot wird beendet)
        """
        import os
        from pathlib import Path

        # ToS-Acceptance File
        tos_file = Path.home() / ".ce365_tos_accepted"

        # Wenn Datei existiert: User hat schon zugestimmt
        if tos_file.exists():
            return True

        # Kompakter Disclaimer
        from rich.panel import Panel

        disclaimer_text = (
            "CE365 Agent wird [bold]AS IS[/bold] bereitgestellt, ohne jegliche Garantie.\n"
            "Keine Haftung fuer Datenverlust, System-Schaeden oder fehlgeschlagene Reparaturen.\n"
            "Nutzung auf eigene Verantwortung — Backups vor Reparaturen sind Pflicht.\n\n"
            "[dim]Vollstaendiger Text: DISCLAIMER.txt[/dim]"
        )
        self.console.console.print()
        self.console.console.print(Panel(disclaimer_text, title="⚠️  Haftungsausschluss", border_style="red"))
        self.console.console.print()

        # User-Eingabe
        while True:
            response = self.console.get_input("Akzeptieren? (ja/nein)").strip().lower()

            if response in ["ja", "yes", "y", "j"]:
                try:
                    tos_file.touch()
                    self.console.display_success("Bedingungen akzeptiert. Viel Erfolg!")
                    self.console.console.print()
                    return True
                except Exception as e:
                    self.console.display_warning(f"Konnte Akzeptanz nicht speichern: {e}")
                    return True

            elif response in ["nein", "no", "n"]:
                self.console.console.print()
                self.console.console.print("[yellow]Bedingungen nicht akzeptiert — CE365 wird beendet.[/yellow]")
                self.console.console.print("[dim]Bei Fragen: https://github.com/eckhardt77/ce365-agent/issues[/dim]")
                self.console.console.print()
                return False

            else:
                self.console.console.print("[red]Bitte antworte mit 'ja' oder 'nein'[/red]\n")

    async def _check_license(self):
        """
        Prüft Lizenz beim Start (wenn License Key gesetzt)

        Features:
        - Online-Validierung via Backend
        - Offline-Fallback mit gecachter Lizenz
        - Edition-Info anzeigen
        """
        settings = get_settings()

        # Edition wurde in __init__ downgraded (Core/Scale ohne Credentials)
        if self._edition_downgraded:
            self.console.display_error(
                "❌ MSP Core/Scale erfordert einen gültigen Lizenzschlüssel und Lizenzserver.\n"
                "   Setze LICENSE_KEY und LICENSE_SERVER_URL in der .env Datei.\n"
                "   Oder nutze EDITION=free für die kostenlose Version."
            )
            import sys
            sys.exit(1)

        # Free braucht keine Lizenz
        if settings.edition == "free":
            self.console.display_info("📦 Edition: Free (keine Lizenz erforderlich)")
            return

        try:
            self.console.console.print("[dim]🔑 Validiere Lizenz...[/dim]")

            # Lizenz validieren
            result = await validate_license(
                license_key=settings.license_key,
                license_server_url=settings.license_server_url,
                timeout=5
            )

            if not result["valid"]:
                self.console.display_error(f"❌ Ungültige Lizenz: {result.get('error', 'Unknown error')}")
                self.console.console.print()
                self.console.console.print("[yellow]Bitte kontaktiere den Support oder prüfe deine Lizenz.[/yellow]")
                self.console.console.print()
                import sys
                sys.exit(1)

            # Edition-Info anzeigen
            edition_names = {
                "free": "Free",
                "core": "MSP Core",
                "scale": "MSP Scale",
            }

            edition_display = edition_names.get(result["edition"], result["edition"])

            self.console.display_success(f"Lizenz gültig: {edition_display}")

            # Offline-Hinweis wenn gecachte Lizenz verwendet
            if result.get("_offline"):
                self.console.display_warning("⚠️  Offline-Modus (gecachte Lizenz)")

            # Ablaufdatum anzeigen
            if result.get("expires_at") and result["expires_at"] != "never":
                from datetime import datetime
                expires_at = datetime.fromisoformat(result["expires_at"])
                self.console.display_info(f"Gültig bis: {expires_at.strftime('%d.%m.%Y')}")

            # Max Systems anzeigen
            if result.get("max_systems") and result["max_systems"] > 0:
                self.console.display_info(f"Max. Systeme: {result['max_systems']}")

        except Exception as e:
            self.console.display_error(f"❌ Lizenz-Check fehlgeschlagen: {str(e)}")
            self.console.console.print()
            import sys
            sys.exit(1)

    async def _display_startup_panel(self):
        """Konsolidiertes Startup-Panel mit allen Infos"""
        import platform
        import psutil
        from ce365.__version__ import __version__

        settings = get_settings()
        lines = []

        # Techniker-Info
        tech_name = settings.technician_name or "Techniker"
        company = settings.company or ""
        tech_line = f"  Techniker: {tech_name}"
        if company:
            tech_line += f" · {company}"
        lines.append(tech_line)
        lines.append(f"  Session: {self.session.session_id[:8]}")
        lines.append("")

        # System-Info (plattformuebergreifend)
        try:
            from ce365.tools.audit.system_info import get_hardware_info
            hw = get_hardware_info()

            os_display = hw["os_name"] or f"{platform.system()} {platform.release()}"
            cpu_display = hw["cpu_name"] or platform.machine()
            ram_gb = hw["ram_total_gb"]

            sys_line = f"  System: {os_display} · {cpu_display} · {ram_gb:.0f} GB RAM"

            # Hersteller/Modell wenn vorhanden (z.B. "Dell Latitude 5540")
            if hw["manufacturer"] or hw["model"]:
                device = f"{hw['manufacturer']} {hw['model']}".strip()
                sys_line += f"\n  Geraet: {device}"

            lines.append(sys_line)
        except Exception:
            lines.append(f"  System: {platform.system()} {platform.release()} · {platform.machine()}")

        # DB + Tools + Learning
        db_type = settings.learning_db_type or "sqlite"
        db_display = "SQLite (lokal)" if db_type == "sqlite" else db_type.upper()

        learning_count = 0
        try:
            stats = self.case_library.get_statistics()
            learning_count = stats.get('total_cases', 0)
        except Exception:
            pass

        lines.append(f"  DB: {db_display} · Tools: {self._tool_count} · Learning: {learning_count} Fälle")

        # Warnungen (dezent)
        warnings = []
        if not PII_AVAILABLE:
            warnings.append("PII Detection nicht verfügbar")
        if db_type == "sqlite" and settings.learning_db_type != "sqlite":
            warnings.append("DB-Fallback auf SQLite")

        if warnings:
            lines.append("")
            for w in warnings:
                lines.append(f"  [dim yellow]⚠ {w}[/dim yellow]")

        lines.append("")
        lines.append("  Tippe /help oder /scan für System-Analyse")

        edition = settings.edition.title()
        title = f"CE365 Agent v{__version__} — {edition} Edition"
        self.console.display_status_panel(lines, title=title)

    async def run_full_scan(self):
        """
        Vollstaendige System-Analyse: delegiert an die 'komplett' Routine.
        """
        from ce365.core.routines import run_routine
        await run_routine(self, "komplett")

    async def process_message(self, user_input: str):
        """
        Message verarbeiten mit Tool Use Loop

        Tool Use Loop:
        1. PII Detection & Anonymisierung
        2. User Message zu History
        3. Claude API Call mit Tools
        4. stop_reason prüfen:
           - "end_turn" → Text-Antwort zurückgeben
           - "tool_use" → handle_tool_use() → rekursiv fortsetzen
        """
        # 1. PII Detection & Anonymisierung
        if self.pii_detector:
            anonymized_input, detections = self.pii_detector.anonymize(user_input)

            # User-Warning anzeigen wenn PII gefunden
            if detections and self.pii_detector.show_warnings:
                warning = self.pii_detector.format_detection_warning(detections)
                self.console.display_warning(warning)

            # Anonymisierten Input verwenden (für Claude API & Learning System)
            processed_input = anonymized_input if detections else user_input
        else:
            # PII Detection deaktiviert
            processed_input = user_input

        # 2. User Message zu History
        self.session.add_message(role="user", content=processed_input)

        # API Call
        try:
            response = self.client.create_message(
                messages=self.session.get_messages(),
                system=self.system_prompt,
                tools=self.tool_registry.get_tool_definitions(),
            )

            # stop_reason prüfen
            if response.stop_reason == "end_turn":
                # Text-Antwort extrahieren
                text_content = self._extract_text(response.content)
                if text_content:
                    self.session.add_message(role="assistant", content=text_content)
                    self.console.display_assistant_message(text_content)

            elif response.stop_reason == "tool_use":
                # Tool Use Loop
                await self.handle_tool_use(response)

            else:
                self.console.display_warning(
                    f"Unbekannter stop_reason: {response.stop_reason}"
                )

        except Exception as e:
            self.console.display_error(f"API Fehler: {str(e)}")

    async def handle_tool_use(self, response):
        """
        Tool Use Blocks verarbeiten

        Flow:
        1. Text + Tool Use Blocks extrahieren
        2. Tools ausführen (mit State Validation)
        3. Tool Results sammeln
        4. Assistant Message + Tool Results zu History
        5. Rekursiv process_message() fortsetzen
        """
        # Text + Tool Use Blocks extrahieren
        text_parts = []
        tool_uses = []

        for block in response.content:
            if block.type == "text":
                text_parts.append(block.text)
            elif block.type == "tool_use":
                tool_uses.append(block)

        # Text anzeigen (wenn vorhanden)
        if text_parts:
            text = "\n".join(text_parts)
            self.console.display_assistant_message(text)

        # Tools ausführen
        tool_results = []
        for tool_use in tool_uses:
            tool_name = tool_use.name
            tool_input = tool_use.input
            tool_id = tool_use.id

            # BUGFIX: XML-Tags aus Tool-Namen entfernen (Claude API Bug)
            if '"' in tool_name or '<' in tool_name or '>' in tool_name:
                # Tool-Name ist korrupt, extrahiere nur den ersten Teil
                tool_name = tool_name.split('"')[0].split('<')[0].strip()
                self.console.display_warning(f"Tool-Name korrigiert zu: {tool_name}")

            # Multi-Agent: consult_specialist direkt hier abfangen
            if tool_name == "consult_specialist":
                specialist_id = tool_input.get("specialist", "")
                task = tool_input.get("task", "")
                context = tool_input.get("context", "")
                result = await self._run_specialist(specialist_id, task, context)
                success = True
            else:
                # Normales Tool ausführen mit Spinner
                with self.console.show_spinner(f"🔧 Executing {tool_name}"):
                    success, result = await self.executor.execute_tool(tool_name, tool_input)

            # Ergebnis anzeigen
            self.console.display_tool_result(tool_name, result, success)

            # Tool Result für API
            tool_results.append(
                {
                    "type": "tool_result",
                    "tool_use_id": tool_id,
                    "content": result,
                }
            )

        # Assistant Message (mit Tool Use) zu History
        self.session.add_message(role="assistant", content=response.content)

        # Tool Results zu History
        self.session.add_message(role="user", content=tool_results)

        # Rekursiv fortsetzen (ohne User Input)
        await self.continue_after_tools()

    async def continue_after_tools(self):
        """Nach Tool Execution fortsetzen"""
        try:
            response = self.client.create_message(
                messages=self.session.get_messages(),
                system=self.system_prompt,
                tools=self.tool_registry.get_tool_definitions(),
            )

            if response.stop_reason == "end_turn":
                text_content = self._extract_text(response.content)
                if text_content:
                    self.session.add_message(role="assistant", content=text_content)
                    self.console.display_assistant_message(text_content)

            elif response.stop_reason == "tool_use":
                # Weitere Tool Calls
                await self.handle_tool_use(response)

        except Exception as e:
            self.console.display_error(f"API Fehler: {str(e)}")

    async def handle_go_repair(self, command: str):
        """
        GO REPAIR Befehl verarbeiten

        Flow:
        1. Parse Command → (approved_steps, freitext)
        2. State Machine Lock aktivieren
        3. Bestätigung anzeigen (nummeriert oder Freitext)
        4. LLM informieren (mit Freitext als Kontext)
        """
        # Parse Command → (steps, freitext)
        parsed = ExecutionLock.parse_go_command(command)

        if not parsed:
            self.console.display_error(
                "Ungültiger GO REPAIR Befehl.\n"
                "Formate: GO REPAIR: 1,2,3 | GO REPAIR: 1-3 | GO REPAIR (alle Schritte)\n"
                "Oder: GO REPAIR: disable Login Items [Notion, Steam]"
            )
            return

        approved_steps, freitext = parsed

        # State Check — automatisch auf PLAN_READY setzen wenn noetig
        current = self.state_machine.current_state
        if current.value == "completed":
            self.console.display_error(
                "Session wurde bereits abgeschlossen. Starte eine neue Session."
            )
            return

        if current.value == "locked":
            # Bereits gelockt — Steps + Freitext aktualisieren und weitermachen
            self.state_machine.approved_steps = approved_steps
            self.state_machine.approval_text = freitext
        elif current.value == "plan_ready":
            # Normaler Flow
            pass
        else:
            # Auto-Transition: Der Bot hat offensichtlich einen Plan praesentiert,
            # auch wenn die State Machine das nicht mitbekommen hat.
            self.state_machine.transition_to_plan_ready("(vom User per GO REPAIR bestätigt)")

        # Lock aktivieren (nur wenn nicht schon locked)
        try:
            if current.value != "locked":
                self.state_machine.lock_execution(approved_steps, freitext)

            # Bestätigung: Freitext vs. nummerierte Schritte
            if freitext:
                self.console.display_success(
                    f"✓ Execution Lock aktiviert — Freigabe: {freitext}"
                )
            else:
                self.console.display_success(
                    f"✓ Execution Lock aktiviert für Schritte: {ExecutionLock.format_steps(approved_steps)}"
                )
            self.console.display_info(
                "Steve kann jetzt die freigegebenen Repair-Tools ausführen."
            )

            # LLM informieren: Freitext vs. nummerierte Schritte
            if freitext:
                approval_message = (
                    f"GO REPAIR Freigabe: {freitext}. "
                    "Führe genau das jetzt aus."
                )
            else:
                approval_message = (
                    f"GO REPAIR Freigabe erhalten für Schritte: {ExecutionLock.format_steps(approved_steps)}. "
                    "Führe jetzt die freigegebenen Schritte aus."
                )
            await self.process_message(approval_message)

        except ValueError as e:
            self.console.display_error(str(e))

    def _extract_text(self, content: List[Any]) -> str:
        """Text aus Content Blocks extrahieren"""
        text_parts = []
        for block in content:
            if hasattr(block, "type") and block.type == "text":
                text_parts.append(block.text)
            elif isinstance(block, str):
                text_parts.append(block)
        return "\n".join(text_parts)

    # ==========================================
    # UPDATE CHECK (passiv beim Start)
    # ==========================================

    def _check_for_updates(self):
        """Passiver Update-Check beim Start (1x pro Tag, nicht-blockierend)"""
        try:
            from ce365.__version__ import __version__
            from ce365.core.updater import check_for_update, _is_binary

            update_info = check_for_update(__version__)
            if update_info and update_info.get("update_available"):
                latest = update_info.get("latest_version", "?")
                if _is_binary():
                    self.console.display_info(
                        f"Neue Version v{latest} verfuegbar! "
                        f"Update mit: ce365 --update"
                    )
                else:
                    self.console.display_info(
                        f"Neue Version v{latest} verfuegbar! "
                        f"Update mit: pip install --upgrade ce365-agent"
                    )
        except Exception:
            pass  # Update-Check darf niemals den Start blockieren

    # ==========================================
    # MULTI-AGENT: SPEZIALIST KONSULTIEREN
    # ==========================================

    async def _run_specialist(self, specialist_id: str, task: str, context: str = "") -> str:
        """
        Spezialist-Agent spawnen und Diagnose durchführen lassen.

        Args:
            specialist_id: "windows", "macos", "network", "security", "performance"
            task: Aufgabe für den Spezialisten
            context: Zusätzlicher Kontext

        Returns:
            Strukturierter Diagnosebericht
        """
        if specialist_id not in SPECIALISTS:
            return (
                f"Unbekannter Spezialist: '{specialist_id}'. "
                f"Verfügbar: {', '.join(SPECIALISTS.keys())}"
            )

        spec_info = SPECIALISTS[specialist_id]
        spec_name = f"{spec_info['emoji']} {spec_info['name']}"

        self.console.display_separator()
        self.console.display_info(f"{spec_name} wird konsultiert...")
        self.console.display_info(f"Aufgabe: {task}")
        if context:
            self.console.display_info(f"Kontext: {context[:100]}...")
        self.console.display_separator()

        try:
            agent = SpecialistAgent(
                specialist_id=specialist_id,
                llm_client=self.client,
                full_tool_registry=self.tool_registry,
                max_rounds=5,
            )

            # Agent ausführen
            report = await agent.run(task=task, context=context)

            self.console.display_separator()
            self.console.display_info(f"{spec_name} — Bericht:")
            self.console.display_separator()

            return f"[Bericht von {spec_info['name']}]\n\n{report}"

        except Exception as e:
            return f"Fehler bei {spec_info['name']}: {str(e)}"

    # ==========================================
    # SESSION MANAGEMENT (Pro)
    # ==========================================

    async def _start_session(self):
        """Startet Session auf Lizenzserver (Pro)"""
        settings = get_settings()
        if not settings.license_server_url:
            return

        try:
            import httpx
            async with httpx.AsyncClient(timeout=5) as client:
                response = await client.post(
                    f"{settings.license_server_url}/api/license/session/start",
                    json={
                        "license_key": settings.license_key,
                        "system_fingerprint": "",
                    },
                )

                if response.status_code == 200:
                    data = response.json()
                    if data.get("success"):
                        self._session_token = data["session_token"]
                        # Heartbeat starten
                        self._heartbeat_task = asyncio.create_task(self._heartbeat_loop())
                    else:
                        self.console.display_error(
                            f"Session konnte nicht gestartet werden: {data.get('error', '')}"
                        )
                        import sys
                        sys.exit(1)
        except Exception as e:
            self.console.display_warning(f"Session-Start fehlgeschlagen: {e}")

    async def _heartbeat_loop(self):
        """Sendet Heartbeat alle 5 Minuten"""
        settings = get_settings()
        while True:
            try:
                await asyncio.sleep(300)  # 5 Minuten
                if not self._session_token:
                    break

                import httpx
                async with httpx.AsyncClient(timeout=5) as client:
                    await client.post(
                        f"{settings.license_server_url}/api/license/session/heartbeat",
                        json={"session_token": self._session_token},
                    )
            except asyncio.CancelledError:
                break
            except Exception:
                pass  # Heartbeat-Fehler ignorieren

    async def _release_session(self):
        """Gibt Session auf Lizenzserver frei"""
        if self._heartbeat_task:
            self._heartbeat_task.cancel()
            self._heartbeat_task = None

        if not self._session_token:
            return

        settings = get_settings()
        if not settings.license_server_url:
            return

        try:
            import httpx
            async with httpx.AsyncClient(timeout=5) as client:
                await client.post(
                    f"{settings.license_server_url}/api/license/session/release",
                    json={"session_token": self._session_token},
                )
        except Exception:
            pass  # Bei Exit ignorieren

        self._session_token = None

    # ==========================================
    # LEARNING SYSTEM METHODS
    # ==========================================

    def _extract_problem_info(self, user_message: str):
        """
        Problem-Info aus User-Message extrahieren

        Versucht zu erkennen:
        - OS-Type (windows/macos)
        - OS-Version
        - Error-Codes
        - Problem-Beschreibung
        """
        message_lower = user_message.lower()

        # OS-Type Detection
        if "windows" in message_lower:
            self.detected_os_type = "windows"
            # Version
            if "windows 11" in message_lower or "win 11" in message_lower:
                self.detected_os_version = "Windows 11"
            elif "windows 10" in message_lower or "win 10" in message_lower:
                self.detected_os_version = "Windows 10"
        elif "macos" in message_lower or "mac os" in message_lower:
            self.detected_os_type = "macos"
            # Version
            if "sequoia" in message_lower or "15" in message_lower:
                self.detected_os_version = "macOS 15 Sequoia"
            elif "sonoma" in message_lower or "14" in message_lower:
                self.detected_os_version = "macOS 14 Sonoma"
            elif "ventura" in message_lower or "13" in message_lower:
                self.detected_os_version = "macOS 13 Ventura"

        # Error-Codes extrahieren (0x..., Error-Code: ...)
        import re
        error_patterns = [
            r'0x[0-9A-Fa-f]+',  # Hex-Codes
        ]

        found_errors = []
        for pattern in error_patterns:
            matches = re.findall(pattern, user_message, re.IGNORECASE)
            found_errors.extend(matches)

        if found_errors:
            # Nur unique Error-Codes, keine Duplikate
            self.error_codes = ", ".join(sorted(set(found_errors)))

        # Kunde/Ticket aus Message extrahieren
        import re as _re
        customer_match = _re.search(r'(?:kunde|customer|client)\s*[:=]\s*(.+?)(?:\n|$)', user_message, _re.IGNORECASE)
        if customer_match:
            self.customer_name = customer_match.group(1).strip()

        ticket_match = _re.search(r'(?:ticket|ticket-id|vorgangs?-?nr|incident)\s*[:=]\s*([A-Za-z0-9\-_]+)', user_message, _re.IGNORECASE)
        if ticket_match:
            self.ticket_id = ticket_match.group(1).strip()

        # Problem-Beschreibung (User-Message als Ganzes)
        if not self.problem_description:
            self.problem_description = user_message

    async def _check_for_similar_cases(self) -> bool:
        """
        Nach ähnlichen Fällen suchen und anbieten

        Returns:
            True wenn ähnlicher Fall gefunden und angeboten
        """
        if not self.detected_os_type or not self.problem_description:
            return False

        # Ähnliche Fälle suchen
        similar_cases = self.case_library.find_similar_cases(
            os_type=self.detected_os_type,
            problem_description=self.problem_description,
            error_code=self.error_codes,
            limit=1,  # Nur bester Match
            min_similarity=0.6  # Mindestens 60% Ähnlichkeit
        )

        if not similar_cases:
            return False

        case, similarity = similar_cases[0]

        # Nur anbieten wenn Similarity hoch genug und Fall bereits wiederverwendet
        if similarity < 0.6:
            return False

        # Bekannte Lösung anbieten
        case_data = {
            'problem_description': case.problem_description,
            'root_cause': case.root_cause,
            'solution_plan': case.solution_plan,
            'reuse_count': case.reuse_count,
            'success_rate': case.success_rate
        }

        self.console.display_known_solution(case_data, similarity)

        # Case ID speichern für späteren Reuse-Tracking
        self.similar_case_offered = case.id

        return True

    async def _save_session_as_case(self, success: bool):
        """
        Session als Fall für Learning speichern

        Wird aufgerufen wenn Session erfolgreich abgeschlossen
        """
        # Nur speichern wenn alle nötigen Infos vorhanden
        if not all([
            self.detected_os_type,
            self.problem_description,
            self.diagnosed_root_cause,
            self.state_machine.repair_plan
        ]):
            self.console.display_warning(
                "⚠️  Session konnte nicht für Learning gespeichert werden (fehlende Informationen)"
            )
            return

        # Duration berechnen
        duration = (datetime.now() - self.session_start_time).total_seconds() / 60

        # Case erstellen
        case = Case(
            os_type=self.detected_os_type,
            os_version=self.detected_os_version or "Unknown",
            problem_description=self.problem_description,
            error_codes=self.error_codes,
            root_cause=self.diagnosed_root_cause,
            solution_plan=self.state_machine.repair_plan,
            executed_steps=str(self.state_machine.executed_steps),
            success=success,
            session_id=self.session.session_id,
            tokens_used=self.client.get_token_usage()['total_tokens'],
            duration_minutes=int(duration)
        )

        try:
            case_id = self.case_library.save_case(case)
            self.console.display_success(
                f"✓ Fall für Learning gespeichert (ID: {case_id})"
            )

            # Wenn bekannte Lösung verwendet wurde, Reuse-Counter aktualisieren
            if self.similar_case_offered and success:
                self.case_library.mark_case_reused(
                    self.similar_case_offered,
                    success=True
                )
                self.console.display_info(
                    f"✓ Wiederverwendungs-Counter aktualisiert (Case {self.similar_case_offered})"
                )

        except Exception as e:
            self.console.display_error(
                f"❌ Fehler beim Speichern für Learning: {str(e)}"
            )

    async def _handle_privacy_command(self):
        """Datenschutz-Menü: Daten anzeigen, exportieren, löschen"""
        from ce365.privacy.data_deletion import DataDeletionManager

        manager = DataDeletionManager()
        summary = manager.get_data_summary()

        self.console.display_separator()
        self.console.console.print("[bold]🔒 Datenschutz — Gespeicherte Daten[/bold]\n")
        self.console.console.print(f"  Changelogs: {summary['changelogs']} Dateien")
        self.console.console.print(f"  Sessions:   {summary['sessions']} Dateien")
        self.console.console.print(f"  Datenbanken: {len(summary['databases'])}")
        self.console.console.print(f"  Cache:      {len(summary['cache_files'])} Dateien")
        self.console.console.print()
        self.console.console.print("  Speicherorte:")
        for loc in summary['storage_locations']:
            self.console.console.print(f"    - {loc}")
        self.console.console.print()

        self.console.console.print("[bold]Optionen:[/bold]")
        self.console.console.print("  1) Daten exportieren (JSON)")
        self.console.console.print("  2) Alle lokalen Daten löschen")
        self.console.console.print("  3) Alte Daten aufräumen (Retention Policy)")
        self.console.console.print("  4) Abbrechen")
        self.console.console.print()

        choice = self.console.get_input("Auswahl (1-4)").strip()

        if choice == "1":
            export = manager.export_all_data()
            export_path = Path("data") / "ce365_data_export.json"
            import json
            with open(export_path, "w", encoding="utf-8") as f:
                json.dump(export, f, indent=2, ensure_ascii=False, default=str)
            self.console.display_success(f"Daten exportiert nach: {export_path}")

        elif choice == "2":
            confirm = self.console.get_input(
                "Alle lokalen Daten unwiderruflich löschen? (ja/nein)"
            ).strip().lower()
            if confirm in ["ja", "j", "yes", "y"]:
                result = manager.delete_all_local_data()
                for item in result["deleted"]:
                    self.console.display_success(f"  Gelöscht: {item}")
                for err in result["errors"]:
                    self.console.display_error(f"  Fehler: {err}")
                self.console.display_success("Alle lokalen Daten gelöscht.")
            else:
                self.console.display_info("Abgebrochen.")

        elif choice == "3":
            result = manager.cleanup_old_data()
            self.console.display_success(
                f"Aufgeräumt: {result['deleted_changelogs']} alte Changelogs gelöscht "
                f"(älter als {result['changelog_cutoff_days']} Tage)"
            )

        else:
            self.console.display_info("Abgebrochen.")
