"""
CE365 Agent - Malware & Antivirus Scan Tool

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

Unterstützt:
- Windows: Windows Defender (nativer Scan)
- macOS/Linux: ClamAV (falls installiert)
- Quarantäne-Funktionalität
"""

import platform
import subprocess
import shutil
from ce365.tools.sanitize import validate_file_path, sanitize_powershell_string
from pathlib import Path
from datetime import datetime
from typing import Optional

from ce365.tools.base import AuditTool
from ce365.i18n import get_translator


class MalwareScanTool(AuditTool):
    """
    Malware & Antivirus Scan

    Scannt System auf Malware, Viren, Trojaner
    """

    name = "scan_malware"
    description = "Scans system for malware, viruses, and trojans"

    input_schema = {
        "type": "object",
        "properties": {
            "scan_type": {
                "type": "string",
                "enum": ["quick", "full", "custom"],
                "description": "Scan type: quick (common locations), full (entire system), custom (specific path)"
            },
            "path": {
                "type": "string",
                "description": "Custom path to scan (only for scan_type=custom)"
            },
            "quarantine": {
                "type": "boolean",
                "description": "Automatically quarantine threats (default: false)",
                "default": False
            }
        },
        "required": ["scan_type"]
    }

    def __init__(self):
        super().__init__()
        self.t = get_translator()
        self.os_type = platform.system()
        self.quarantine_dir = Path.home() / ".ce365" / "quarantine"
        self.quarantine_dir.mkdir(parents=True, exist_ok=True)

    async def execute(self, scan_type: str, path: Optional[str] = None, quarantine: bool = False) -> str:
        """
        Führt Malware-Scan durch

        Args:
            scan_type: "quick", "full" oder "custom"
            path: Pfad für custom Scan
            quarantine: Bedrohungen automatisch in Quarantäne

        Returns:
            Scan-Ergebnis als formatierter String
        """

        # SCHRITT 1: Virus-Definitionen updaten (KRITISCH!)
        print("📥 Updating virus definitions...")

        update_result = None
        if self.os_type == "Windows":
            update_result = await self._update_windows_defender()
        elif self.os_type in ["Darwin", "Linux"]:
            update_result = await self._update_clamav()

        # SCHRITT 2: Scan durchführen
        if self.os_type == "Windows":
            result = await self._scan_windows_defender(scan_type, path)
        elif self.os_type == "Darwin":  # macOS
            result = await self._scan_clamav(scan_type, path)
        elif self.os_type == "Linux":
            result = await self._scan_clamav(scan_type, path)
        else:
            return f"❌ Unsupported OS: {self.os_type}"

        # Update-Status in Result einbauen
        if update_result:
            result["definitions_updated"] = update_result.get("success", False)
            result["definitions_age"] = update_result.get("age", "unknown")
            result["last_update"] = update_result.get("last_update", "unknown")

        # SCHRITT 3: Quarantäne falls gewünscht
        if quarantine and result.get("threats"):
            quarantined = self._quarantine_threats(result["threats"])
            result["quarantined"] = quarantined

        return self._format_result(result)

    def _format_result(self, result: dict) -> str:
        """Formatiert das Scan-Ergebnis als lesbaren String"""
        if result.get("status") == "error":
            return f"❌ Scan-Fehler: {result.get('message', 'Unbekannter Fehler')}"

        lines = []
        lines.append("🛡️  MALWARE-SCAN ERGEBNIS")
        lines.append("=" * 50)
        lines.append(f"Scanner: {result.get('scanner', '?')}")
        lines.append(f"Scan-Typ: {result.get('scan_type', '?')}")

        if result.get("definitions_updated") is not None:
            status = "✅ Aktuell" if result["definitions_updated"] else "⚠️  Nicht aktualisiert"
            lines.append(f"Definitionen: {status}")
            if result.get("definitions_age") and result["definitions_age"] != "unknown":
                lines.append(f"Definitions-Alter: {result['definitions_age']}")

        lines.append("")

        if result.get("clean"):
            lines.append("✅ Keine Bedrohungen gefunden — System sauber")
        else:
            count = result.get("threats_found", 0)
            lines.append(f"⚠️  {count} Bedrohung(en) gefunden:")
            lines.append("")
            for threat in result.get("threats", []):
                lines.append(f"  🔴 {threat.get('name', '?')}")
                lines.append(f"     Pfad: {threat.get('path', '?')}")
                lines.append(f"     Schwere: {threat.get('severity', '?')}")

        if result.get("quarantined"):
            lines.append("")
            lines.append(f"🔒 {len(result['quarantined'])} Datei(en) in Quarantäne verschoben")

        return "\n".join(lines)

    async def _update_windows_defender(self) -> dict:
        """
        Windows Defender Virus-Definitionen updaten

        Returns:
            Dict mit Update-Status
        """

        try:
            # Definitionen updaten
            print("   Updating Windows Defender signatures...")

            cmd = ["powershell", "-Command", "Update-MpSignature"]

            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=300  # 5 Minuten Timeout
            )

            # Prüfe aktuellen Status
            status_cmd = [
                "powershell", "-Command",
                "Get-MpComputerStatus | Select-Object AntivirusSignatureLastUpdated, AntivirusSignatureAge | ConvertTo-Json"
            ]

            status_result = subprocess.run(
                status_cmd,
                capture_output=True,
                text=True,
                timeout=10
            )

            if status_result.returncode == 0 and status_result.stdout.strip():
                import json
                status = json.loads(status_result.stdout)

                last_update = status.get("AntivirusSignatureLastUpdated", "unknown")
                age_days = status.get("AntivirusSignatureAge", 0)

                if age_days == 0:
                    print(f"   ✅ Definitions up-to-date (last update: {last_update})")
                elif age_days <= 1:
                    print(f"   ✅ Definitions updated (age: {age_days} day)")
                else:
                    print(f"   ⚠️  Definitions outdated (age: {age_days} days)")

                return {
                    "success": True,
                    "last_update": last_update,
                    "age": f"{age_days} days"
                }

            return {
                "success": result.returncode == 0,
                "last_update": "unknown",
                "age": "unknown"
            }

        except subprocess.TimeoutExpired:
            print("   ⚠️  Update timeout (slow connection?)")
            return {"success": False, "error": "timeout"}
        except Exception as e:
            print(f"   ⚠️  Update failed: {e}")
            return {"success": False, "error": str(e)}

    async def _update_clamav(self) -> dict:
        """
        ClamAV Virus-Definitionen updaten via freshclam

        Returns:
            Dict mit Update-Status
        """

        # Prüfe ob freshclam verfügbar
        if not shutil.which("freshclam"):
            print("   ⚠️  freshclam not found - definitions may be outdated")
            return {
                "success": False,
                "error": "freshclam not installed",
                "last_update": "unknown",
                "age": "unknown"
            }

        try:
            print("   Updating ClamAV signatures...")

            # freshclam ausführen
            cmd = ["freshclam", "--quiet"]

            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=300  # 5 Minuten
            )

            # Prüfe Status
            if result.returncode == 0:
                print("   ✅ ClamAV definitions updated successfully")

                # Versuche Alter der Definitionen zu ermitteln
                db_path = Path("/usr/local/share/clamav/daily.cvd")  # macOS Homebrew
                if not db_path.exists():
                    db_path = Path("/var/lib/clamav/daily.cvd")  # Linux

                if db_path.exists():
                    import time
                    age_seconds = time.time() - db_path.stat().st_mtime
                    age_hours = int(age_seconds / 3600)

                    if age_hours < 24:
                        print(f"   ✅ Definitions age: {age_hours} hours")
                    else:
                        age_days = int(age_hours / 24)
                        print(f"   ⚠️  Definitions age: {age_days} days")

                    return {
                        "success": True,
                        "last_update": datetime.fromtimestamp(db_path.stat().st_mtime).isoformat(),
                        "age": f"{age_hours} hours" if age_hours < 24 else f"{age_days} days"
                    }

                return {"success": True, "last_update": "now", "age": "0 hours"}

            elif "already" in result.stdout.lower() or "up-to-date" in result.stdout.lower():
                print("   ✅ ClamAV definitions already up-to-date")
                return {"success": True, "last_update": "recent", "age": "< 1 day"}

            else:
                print(f"   ⚠️  Update warning: {result.stderr[:100]}")
                return {"success": False, "error": result.stderr[:200]}

        except subprocess.TimeoutExpired:
            print("   ⚠️  freshclam timeout (slow connection?)")
            return {"success": False, "error": "timeout"}
        except Exception as e:
            print(f"   ⚠️  freshclam failed: {e}")
            return {"success": False, "error": str(e)}

    async def _scan_windows_defender(self, scan_type: str, path: Optional[str] = None) -> dict:
        """
        Windows Defender Scan

        Nutzt Windows Defender PowerShell API
        """

        # Check ob Windows Defender verfügbar
        try:
            subprocess.run(
                ["powershell", "-Command", "Get-MpComputerStatus"],
                capture_output=True,
                check=True,
                timeout=5
            )
        except (subprocess.CalledProcessError, FileNotFoundError):
            return {
                "status": "error",
                "message": "Windows Defender not available. Please enable Windows Security."
            }

        # Scan-Command bauen
        if scan_type == "quick":
            cmd = ["powershell", "-Command", "Start-MpScan -ScanType QuickScan"]
        elif scan_type == "full":
            cmd = ["powershell", "-Command", "Start-MpScan -ScanType FullScan"]
        elif scan_type == "custom" and path:
            safe_path = sanitize_powershell_string(validate_file_path(path))
            cmd = ["powershell", "-Command", f"Start-MpScan -ScanType CustomScan -ScanPath '{safe_path}'"]
        else:
            return {"status": "error", "message": "Invalid scan_type or missing path"}

        # Scan durchführen
        print(self.t.t("malware.scan_start"))

        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=3600  # 1 Stunde Timeout
            )

            # Scan-Ergebnis abrufen
            threats_cmd = [
                "powershell", "-Command",
                "Get-MpThreat | Select-Object ThreatName, Resources, @{Name='Severity';Expression={$_.SeverityID}}"
            ]

            threats_result = subprocess.run(
                threats_cmd,
                capture_output=True,
                text=True,
                timeout=30
            )

            # Parse Threats
            threats = self._parse_defender_threats(threats_result.stdout)

            if threats:
                print(self.t.t("malware.scan_complete", threats=len(threats)))
            else:
                print(self.t.t("malware.no_threats"))

            return {
                "status": "success",
                "scanner": "Windows Defender",
                "scan_type": scan_type,
                "threats_found": len(threats),
                "threats": threats,
                "clean": len(threats) == 0
            }

        except subprocess.TimeoutExpired:
            return {
                "status": "error",
                "message": "Scan timeout (1 hour exceeded)"
            }
        except Exception as e:
            return {
                "status": "error",
                "message": f"Scan failed: {str(e)}"
            }

    async def _scan_clamav(self, scan_type: str, path: Optional[str] = None) -> dict:
        """
        ClamAV Scan (macOS/Linux)

        Requires: brew install clamav (macOS) or apt install clamav (Linux)
        """

        # Check ob ClamAV installiert
        if not shutil.which("clamscan"):
            return {
                "status": "error",
                "message": "ClamAV not installed. Install with: brew install clamav (macOS) or apt install clamav (Linux)"
            }

        # Scan-Pfad bestimmen
        if scan_type == "quick":
            scan_path = str(Path.home())  # Home-Verzeichnis
        elif scan_type == "full":
            scan_path = "/"  # Root
        elif scan_type == "custom" and path:
            scan_path = path
        else:
            return {"status": "error", "message": "Invalid scan_type or missing path"}

        # ClamAV Scan
        print(self.t.t("malware.scan_start"))

        try:
            cmd = [
                "clamscan",
                "-r",  # Recursive
                "-i",  # Only show infected files
                "--max-filesize=100M",  # Skip large files
                scan_path
            ]

            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=3600  # 1 Stunde
            )

            # Parse Output
            threats = self._parse_clamav_output(result.stdout)

            if threats:
                print(self.t.t("malware.scan_complete", threats=len(threats)))
            else:
                print(self.t.t("malware.no_threats"))

            return {
                "status": "success",
                "scanner": "ClamAV",
                "scan_type": scan_type,
                "threats_found": len(threats),
                "threats": threats,
                "clean": len(threats) == 0
            }

        except subprocess.TimeoutExpired:
            return {
                "status": "error",
                "message": "Scan timeout (1 hour exceeded)"
            }
        except Exception as e:
            return {
                "status": "error",
                "message": f"Scan failed: {str(e)}"
            }

    def _parse_defender_threats(self, output: str) -> list:
        """Parst Windows Defender Threat-Output"""
        threats = []

        lines = output.strip().split('\n')
        for line in lines[2:]:  # Skip Header
            if line.strip():
                parts = line.split()
                if len(parts) >= 2:
                    threats.append({
                        "name": parts[0],
                        "path": ' '.join(parts[1:-1]) if len(parts) > 2 else parts[1],
                        "severity": parts[-1] if len(parts) > 2 else "Unknown"
                    })

        return threats

    def _parse_clamav_output(self, output: str) -> list:
        """Parst ClamAV Output"""
        threats = []

        for line in output.split('\n'):
            if "FOUND" in line:
                # Format: /path/to/file: Virus.Name FOUND
                parts = line.split(':')
                if len(parts) >= 2:
                    file_path = parts[0].strip()
                    virus_name = parts[1].replace("FOUND", "").strip()

                    threats.append({
                        "name": virus_name,
                        "path": file_path,
                        "severity": "High"
                    })

        return threats

    def _quarantine_threats(self, threats: list) -> list:
        """
        Verschiebt Bedrohungen in Quarantäne

        Args:
            threats: Liste von Threat-Dicts mit "path"

        Returns:
            Liste erfolgreich quarantinierter Dateien
        """
        quarantined = []

        for threat in threats:
            try:
                file_path = Path(threat["path"])

                if not file_path.exists():
                    continue

                # Quarantäne-Dateiname mit Timestamp
                timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
                quarantine_file = self.quarantine_dir / f"{timestamp}_{file_path.name}"

                # Datei verschieben
                shutil.move(str(file_path), str(quarantine_file))

                quarantined.append({
                    "original_path": str(file_path),
                    "quarantine_path": str(quarantine_file),
                    "threat_name": threat["name"]
                })

                print(self.t.t("malware.quarantine_success", file=file_path.name))

            except Exception as e:
                print(f"⚠️  Quarantine failed for {threat['path']}: {e}")

        return quarantined
