"""
2600 Mode CLI Commands - Security research command group

Provides CLI interface for:
- Hash identification and cracking
- Wallet recovery
- Binary analysis
- CTF utilities
- Malware analysis
"""

import sys
from pathlib import Path
from typing import Optional

import click
from rich.console import Console
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn
from rich.table import Table

console = Console()


@click.group("2600")
def mode2600():
    """2600 Mode - Security research and CTF tools.

    Named after 2600 Magazine. For authorized security research only.

    Features:
    - Hash identification and cracking
    - Wallet/seed recovery (personal)
    - Binary analysis and reverse engineering
    - CTF challenge utilities
    - Malware analysis (static)

    Example:
        groknroll 2600 status
        groknroll 2600 hash identify "5d41402abc4b2a76b9719d911017c592"
        groknroll 2600 analyze /path/to/binary
    """
    pass


@mode2600.command("status")
def mode2600_status():
    """Show 2600 mode status and available tools.

    Example:
        groknroll 2600 status
    """
    from groknroll.mode2600.core import get_mode2600

    try:
        mode = get_mode2600()
        stats = mode.get_stats()
        tools = mode.list_available_tools()

        console.print()
        console.print(
            Panel.fit(
                f"[bold]Context Active:[/bold] {'Yes' if stats['context_active'] else 'No'}\n"
                f"[bold]Operations Logged:[/bold] {stats['operations_logged']}\n"
                f"[bold]Config Dir:[/bold] {stats['config_dir']}",
                title="[bold cyan]2600 Mode Status[/bold cyan]",
                border_style="cyan",
            )
        )

        if stats['context_active'] and stats['context']:
            ctx = stats['context']
            console.print(
                f"\n[bold]Authorization:[/bold] {ctx['authorization']}\n"
                f"[bold]Scope:[/bold] {ctx['scope']}"
            )

        # Tool availability
        console.print("\n[bold]Security Tools:[/bold]")
        table = Table()
        table.add_column("Tool", style="cyan")
        table.add_column("Status")

        for tool, available in sorted(tools.items()):
            status = "[green]Available[/green]" if available else "[dim]Not found[/dim]"
            table.add_row(tool, status)

        console.print(table)

    except Exception as e:
        console.print(f"[bold red]Error:[/bold red] {e}")
        sys.exit(1)


@mode2600.command("init")
@click.option(
    "--auth",
    "-a",
    type=click.Choice(["ctf", "personal", "pentest", "malware", "research", "education"]),
    required=True,
    help="Authorization context",
)
@click.option(
    "--scope",
    "-s",
    required=True,
    help="Description of authorized scope",
)
def mode2600_init(auth: str, scope: str):
    """Initialize 2600 mode with authorization context.

    Example:
        groknroll 2600 init --auth ctf --scope "PicoCTF 2024"
        groknroll 2600 init --auth personal --scope "Recovering my old wallet"
    """
    from groknroll.mode2600.core import (
        AuthorizationType,
        SecurityContext,
        get_mode2600,
    )

    auth_map = {
        "ctf": AuthorizationType.CTF_COMPETITION,
        "personal": AuthorizationType.PERSONAL_RECOVERY,
        "pentest": AuthorizationType.AUTHORIZED_PENTEST,
        "malware": AuthorizationType.MALWARE_ANALYSIS,
        "research": AuthorizationType.SECURITY_RESEARCH,
        "education": AuthorizationType.EDUCATION_LAB,
    }

    try:
        mode = get_mode2600()
        context = SecurityContext(
            authorization=auth_map[auth],
            scope=scope,
            user_consent=True,
        )
        mode.set_context(context)

        console.print(f"[green]✓[/green] 2600 mode initialized")
        console.print(f"  Authorization: {auth}")
        console.print(f"  Scope: {scope}")

    except Exception as e:
        console.print(f"[bold red]Error:[/bold red] {e}")
        sys.exit(1)


# =============================================================================
# HASH COMMANDS
# =============================================================================


@mode2600.group("hash")
def hash_group():
    """Hash identification and cracking utilities.

    Example:
        groknroll 2600 hash identify "5d41402abc4b2a76b9719d911017c592"
        groknroll 2600 hash crack "5d41402abc4b2a76b9719d911017c592"
    """
    pass


@hash_group.command("identify")
@click.argument("hash_value")
def hash_identify(hash_value: str):
    """Identify hash type.

    Example:
        groknroll 2600 hash identify "5d41402abc4b2a76b9719d911017c592"
    """
    from groknroll.mode2600.hash_tools import identify_hash

    try:
        result = identify_hash(hash_value)

        console.print()
        console.print(f"[bold]Hash:[/bold] {result.hash_value}")
        console.print(f"[bold]Length:[/bold] {result.length}")
        console.print(f"[bold]Charset:[/bold] {result.charset}")
        console.print(f"[bold]Confidence:[/bold] {result.confidence:.0%}")

        console.print("\n[bold]Possible Types:[/bold]")
        for t in result.possible_types[:5]:
            console.print(f"  • {t.value}")

        if result.recommendations:
            console.print("\n[bold]Recommendations:[/bold]")
            for rec in result.recommendations:
                console.print(f"  • {rec}")

    except Exception as e:
        console.print(f"[bold red]Error:[/bold red] {e}")
        sys.exit(1)


@hash_group.command("crack")
@click.argument("hash_value")
@click.option("--wordlist", "-w", help="Path to wordlist")
@click.option("--type", "-t", "hash_type", help="Hash type (e.g., md5, sha256)")
@click.option("--timeout", default=300, help="Timeout in seconds")
def hash_crack(
    hash_value: str,
    wordlist: Optional[str],
    hash_type: Optional[str],
    timeout: int,
):
    """Crack a hash (for personal password recovery).

    Example:
        groknroll 2600 hash crack "5d41402abc4b2a76b9719d911017c592" -w rockyou.txt
    """
    from groknroll.mode2600.core import get_mode2600
    from groknroll.mode2600.hash_tools import HashCracker, HashType

    # Check authorization
    mode = get_mode2600()
    if not mode.get_context():
        console.print("[yellow]Warning:[/yellow] No authorization context set.")
        console.print("Run: groknroll 2600 init --auth personal --scope 'your scope'")
        sys.exit(1)

    try:
        cracker = HashCracker()

        # Parse hash type
        ht = None
        if hash_type:
            try:
                ht = HashType(hash_type.lower())
            except ValueError:
                console.print(f"[yellow]Unknown hash type: {hash_type}[/yellow]")

        with Progress(
            SpinnerColumn(),
            TextColumn("[progress.description]{task.description}"),
            console=console,
        ) as progress:
            task = progress.add_task("Cracking hash...", total=None)
            result = cracker.crack(hash_value, ht, wordlist, timeout=timeout)

        console.print()

        if result.cracked:
            console.print(f"[bold green]✓ CRACKED![/bold green]")
            console.print(f"  Plaintext: [cyan]{result.plaintext}[/cyan]")
            console.print(f"  Method: {result.method}")
            console.print(f"  Duration: {result.duration_seconds:.1f}s")
        else:
            console.print(f"[yellow]✗ Not cracked[/yellow]")
            console.print(f"  Method tried: {result.method}")
            console.print(f"  Duration: {result.duration_seconds:.1f}s")
            if result.attempts:
                console.print(f"  Attempts: {result.attempts:,}")

    except Exception as e:
        console.print(f"[bold red]Error:[/bold red] {e}")
        sys.exit(1)


@hash_group.command("generate")
@click.argument("plaintext")
@click.option("--type", "-t", "hash_type", default="md5", help="Hash type")
def hash_generate(plaintext: str, hash_type: str):
    """Generate a hash (for testing).

    Example:
        groknroll 2600 hash generate "hello" --type sha256
    """
    from groknroll.mode2600.hash_tools import HashCracker, HashType

    try:
        cracker = HashCracker()
        ht = HashType(hash_type.lower())
        result = cracker.generate_hash(plaintext, ht)

        console.print(f"[bold]Plaintext:[/bold] {plaintext}")
        console.print(f"[bold]Type:[/bold] {hash_type}")
        console.print(f"[bold]Hash:[/bold] {result}")

    except Exception as e:
        console.print(f"[bold red]Error:[/bold red] {e}")
        sys.exit(1)


# =============================================================================
# BINARY ANALYSIS COMMANDS
# =============================================================================


@mode2600.command("analyze")
@click.argument("binary_path", type=click.Path(exists=True))
@click.option("--strings", "-s", is_flag=True, help="Extract strings")
@click.option("--functions", "-f", is_flag=True, help="List functions")
def analyze_binary(binary_path: str, strings: bool, functions: bool):
    """Analyze a binary file.

    Example:
        groknroll 2600 analyze /path/to/binary
        groknroll 2600 analyze /path/to/binary --strings --functions
    """
    from groknroll.mode2600.core import get_mode2600
    from groknroll.mode2600.reverse_engineering import BinaryAnalyzer

    mode = get_mode2600()
    if not mode.get_context():
        console.print("[yellow]Warning:[/yellow] No authorization context set.")

    try:
        analyzer = BinaryAnalyzer()

        with Progress(
            SpinnerColumn(),
            TextColumn("[progress.description]{task.description}"),
            console=console,
        ) as progress:
            task = progress.add_task("Analyzing binary...", total=None)
            info = analyzer.analyze(binary_path)

        console.print()
        console.print(
            Panel.fit(
                f"[bold]File:[/bold] {info.path.name}\n"
                f"[bold]Format:[/bold] {info.format.value}\n"
                f"[bold]Architecture:[/bold] {info.architecture.value} ({info.bits}-bit)\n"
                f"[bold]Endianness:[/bold] {info.endianness}\n"
                f"[bold]Entry Point:[/bold] 0x{info.entry_point or 0:x}\n"
                f"[bold]Stripped:[/bold] {info.stripped}\n"
                f"[bold]PIE:[/bold] {info.pie}\n"
                f"[bold]NX:[/bold] {info.nx}\n"
                f"[bold]Canary:[/bold] {info.canary}",
                title="[bold cyan]Binary Analysis[/bold cyan]",
                border_style="cyan",
            )
        )

        # Libraries
        if info.libraries:
            console.print("\n[bold]Libraries:[/bold]")
            for lib in info.libraries[:10]:
                console.print(f"  • {lib}")

        # Imports
        if info.imports:
            console.print(f"\n[bold]Imports:[/bold] ({len(info.imports)} total)")
            for imp in info.imports[:10]:
                console.print(f"  • {imp}")

        # Strings
        if strings:
            console.print("\n[bold]Interesting Strings:[/bold]")
            extracted = analyzer.extract_strings(min_length=8)
            for s in extracted[:20]:
                if s.entropy > 3.5:
                    console.print(f"  [{s.entropy:.1f}] {s.value[:60]}")

        # Functions
        if functions:
            console.print("\n[bold]Functions:[/bold]")
            funcs = analyzer.find_functions()
            for f in funcs[:20]:
                console.print(f"  0x{f.address:x}: {f.name} ({f.size} bytes)")

    except Exception as e:
        console.print(f"[bold red]Error:[/bold red] {e}")
        sys.exit(1)


@mode2600.command("disasm")
@click.argument("binary_path", type=click.Path(exists=True))
@click.option("--address", "-a", default="0", help="Start address (hex)")
@click.option("--function", "-f", "func", help="Function name")
@click.option("--count", "-n", default=50, help="Number of instructions")
def disassemble(binary_path: str, address: str, func: Optional[str], count: int):
    """Disassemble binary code.

    Example:
        groknroll 2600 disasm /path/to/binary --function main
        groknroll 2600 disasm /path/to/binary --address 0x1000 --count 100
    """
    from groknroll.mode2600.reverse_engineering import BinaryAnalyzer

    try:
        analyzer = BinaryAnalyzer()
        analyzer.analyze(binary_path)

        addr = int(address, 16) if address.startswith("0x") else int(address)
        instructions = analyzer.disassemble(address=addr, length=count, function=func)

        console.print()

        if func:
            console.print(f"[bold]Function: {func}[/bold]\n")
        else:
            console.print(f"[bold]Address: 0x{addr:x}[/bold]\n")

        for inst in instructions:
            console.print(str(inst))

    except Exception as e:
        console.print(f"[bold red]Error:[/bold red] {e}")
        sys.exit(1)


# =============================================================================
# MALWARE ANALYSIS COMMANDS
# =============================================================================


@mode2600.command("malware")
@click.argument("sample_path", type=click.Path(exists=True))
@click.option("--no-yara", is_flag=True, help="Skip YARA scanning")
@click.option("--virustotal", "-v", is_flag=True, help="Check VirusTotal")
def malware_analyze(sample_path: str, no_yara: bool, virustotal: bool):
    """Analyze a malware sample (static analysis only).

    Example:
        groknroll 2600 malware /path/to/sample.exe
        groknroll 2600 malware /path/to/sample.exe --virustotal
    """
    from groknroll.mode2600.core import (
        AuthorizationType,
        SecurityContext,
        get_mode2600,
    )
    from groknroll.mode2600.malware_sandbox import MalwareSandbox

    # Require malware analysis context
    mode = get_mode2600()
    if not mode.get_context():
        # Auto-set context for malware analysis
        mode.set_context(SecurityContext(
            authorization=AuthorizationType.MALWARE_ANALYSIS,
            scope="Static malware analysis",
            user_consent=True,
        ))

    try:
        sandbox = MalwareSandbox()

        with Progress(
            SpinnerColumn(),
            TextColumn("[progress.description]{task.description}"),
            console=console,
        ) as progress:
            task = progress.add_task("Analyzing sample...", total=None)
            result = sandbox.analyze(sample_path, run_yara=not no_yara)

            if virustotal:
                progress.update(task, description="Checking VirusTotal...")
                vt_result = sandbox.check_virustotal(result.indicators.sha256)
                if vt_result:
                    result.virustotal_score = f"{vt_result['malicious']}/{vt_result['malicious'] + vt_result['undetected']}"

        # Display results
        console.print()

        threat_colors = {
            "critical": "red",
            "high": "yellow",
            "medium": "blue",
            "low": "dim",
            "info": "dim",
        }
        threat_color = threat_colors.get(result.threat_level.value, "white")

        console.print(
            Panel.fit(
                f"[bold]File:[/bold] {result.filename}\n"
                f"[bold]Size:[/bold] {result.filesize:,} bytes\n"
                f"[bold]Type:[/bold] {result.filetype[:60]}\n"
                f"[bold]Category:[/bold] {result.category.value}\n"
                f"[bold]Threat Level:[/bold] [{threat_color}]{result.threat_level.value.upper()}[/{threat_color}]\n"
                f"[bold]Static Score:[/bold] {result.static_score:.0f}/100\n"
                f"[bold]Packed:[/bold] {result.packed}{f' ({result.packer})' if result.packer else ''}",
                title="[bold cyan]Malware Analysis (Static)[/bold cyan]",
                border_style=threat_color,
            )
        )

        # Hashes
        console.print("\n[bold]Hashes:[/bold]")
        console.print(f"  MD5:    {result.indicators.md5}")
        console.print(f"  SHA1:   {result.indicators.sha1}")
        console.print(f"  SHA256: {result.indicators.sha256}")

        # VirusTotal
        if result.virustotal_score:
            console.print(f"\n[bold]VirusTotal:[/bold] {result.virustotal_score} detections")

        # IOCs
        iocs = result.indicators
        if iocs.ips or iocs.domains or iocs.urls:
            console.print("\n[bold]Network Indicators:[/bold]")
            for ip in iocs.ips[:5]:
                console.print(f"  [IP] {ip}")
            for domain in iocs.domains[:5]:
                console.print(f"  [Domain] {domain}")
            for url in iocs.urls[:3]:
                console.print(f"  [URL] {url[:60]}")

        if iocs.bitcoin_addresses or iocs.monero_addresses:
            console.print("\n[bold]Crypto Addresses:[/bold]")
            for addr in iocs.bitcoin_addresses[:3]:
                console.print(f"  [BTC] {addr}")
            for addr in iocs.monero_addresses[:3]:
                console.print(f"  [XMR] {addr}")

        # YARA
        if result.yara_matches:
            console.print("\n[bold]YARA Matches:[/bold]")
            for match in result.yara_matches[:10]:
                console.print(f"  • {match.rule_name}")

        # Suspicious imports
        if result.suspicious_imports:
            console.print("\n[bold]Suspicious Imports:[/bold]")
            for imp in result.suspicious_imports[:10]:
                console.print(f"  • {imp}")

        # Suspicious strings
        if result.suspicious_strings:
            console.print("\n[bold]Suspicious Strings:[/bold]")
            for s in result.suspicious_strings[:10]:
                console.print(f"  • {s}")

    except Exception as e:
        console.print(f"[bold red]Error:[/bold red] {e}")
        sys.exit(1)


# =============================================================================
# CTF COMMANDS
# =============================================================================


@mode2600.group("ctf")
def ctf_group():
    """CTF challenge utilities.

    Example:
        groknroll 2600 ctf decode "SGVsbG8gV29ybGQ="
        groknroll 2600 ctf caesar "Uryyb Jbeyq"
    """
    pass


@ctf_group.command("decode")
@click.argument("data")
def ctf_decode(data: str):
    """Try all common decodings on data.

    Example:
        groknroll 2600 ctf decode "SGVsbG8gV29ybGQ="
    """
    from groknroll.mode2600.ctf_tools import CTFHelper

    try:
        helper = CTFHelper()
        results = helper.decode_all(data)

        console.print()
        console.print(f"[bold]Input:[/bold] {data[:50]}{'...' if len(data) > 50 else ''}")
        console.print()

        if results:
            console.print("[bold]Decoded:[/bold]")
            for encoding, decoded in results.items():
                console.print(f"  [{encoding}] {decoded[:60]}")
        else:
            console.print("[yellow]No successful decodings[/yellow]")

        # Check for flags
        flags = helper.find_flags(data)
        for encoding, decoded in results.items():
            flags.extend(helper.find_flags(decoded))

        if flags:
            console.print("\n[bold green]Flags Found:[/bold green]")
            for flag in flags:
                console.print(f"  {flag.flag}")

    except Exception as e:
        console.print(f"[bold red]Error:[/bold red] {e}")
        sys.exit(1)


@ctf_group.command("caesar")
@click.argument("ciphertext")
def ctf_caesar(ciphertext: str):
    """Brute force Caesar cipher.

    Example:
        groknroll 2600 ctf caesar "Uryyb Jbeyq"
    """
    from groknroll.mode2600.ctf_tools import CryptoSolver

    try:
        solver = CryptoSolver()
        results = solver.caesar_bruteforce(ciphertext)

        console.print()
        console.print(f"[bold]Ciphertext:[/bold] {ciphertext}")
        console.print("\n[bold]All rotations:[/bold]")

        for shift, plaintext in results:
            marker = "[green]<--[/green]" if "flag" in plaintext.lower() else ""
            console.print(f"  ROT{shift:2d}: {plaintext} {marker}")

    except Exception as e:
        console.print(f"[bold red]Error:[/bold red] {e}")
        sys.exit(1)


@ctf_group.command("xor")
@click.argument("hex_data")
def ctf_xor(hex_data: str):
    """Single-byte XOR brute force.

    Example:
        groknroll 2600 ctf xor "48656c6c6f"
    """
    from groknroll.mode2600.ctf_tools import CryptoSolver

    try:
        data = bytes.fromhex(hex_data.replace(" ", ""))
        solver = CryptoSolver()
        results = solver.xor_single_byte(data)

        console.print()
        console.print(f"[bold]Input:[/bold] {hex_data[:40]}")
        console.print("\n[bold]Top results:[/bold]")

        for key, result in results:
            try:
                text = result.decode("utf-8", errors="replace")[:50]
                console.print(f"  Key 0x{key:02x}: {text}")
            except:
                console.print(f"  Key 0x{key:02x}: {result.hex()[:40]}")

    except Exception as e:
        console.print(f"[bold red]Error:[/bold red] {e}")
        sys.exit(1)


@ctf_group.command("pattern")
@click.argument("length", type=int)
def ctf_pattern(length: int):
    """Generate cyclic pattern for buffer overflow.

    Example:
        groknroll 2600 ctf pattern 200
    """
    from groknroll.mode2600.ctf_tools import PwnHelper

    try:
        helper = PwnHelper()
        pattern = helper.cyclic_pattern(length)

        console.print()
        console.print(f"[bold]Pattern ({length} bytes):[/bold]")
        console.print(pattern.decode("ascii"))

    except Exception as e:
        console.print(f"[bold red]Error:[/bold red] {e}")
        sys.exit(1)


@ctf_group.command("offset")
@click.argument("pattern")
@click.argument("value")
def ctf_offset(pattern: str, value: str):
    """Find offset in cyclic pattern.

    Example:
        groknroll 2600 ctf offset "aaaabaaacaaa..." "0x61616166"
    """
    from groknroll.mode2600.ctf_tools import PwnHelper

    try:
        helper = PwnHelper()

        # Parse value
        if value.startswith("0x"):
            val = int(value, 16)
        else:
            val = value.encode()

        offset = helper.cyclic_find(pattern.encode(), val)

        console.print()
        if offset >= 0:
            console.print(f"[green]Offset found:[/green] {offset}")
        else:
            console.print("[yellow]Value not found in pattern[/yellow]")

    except Exception as e:
        console.print(f"[bold red]Error:[/bold red] {e}")
        sys.exit(1)
