#!/usr/bin/env python3
"""
behavior_profiler.py - Dynamic behavior profiling infrastructure for PCIe devices

This module provides runtime device behavior monitoring, timing pattern capture,
and behavioral pattern analysis to improve donor device matching accuracy.

Usage:
    from behavior_profiler import BehaviorProfiler

    profiler = BehaviorProfiler(bdf="0000:03:00.0")
    profile = profiler.capture_behavior_profile(duration=30)
    patterns = profiler.analyze_patterns(profile)
"""

import json
import platform
import queue
import re
import statistics
import subprocess
import threading
import time
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple

# Import manufacturing variance simulation
try:
    from .manufacturing_variance import (
        DeviceClass,
        ManufacturingVarianceSimulator,
        VarianceModel,
    )
except ImportError:
    # Fallback for direct execution
    from manufacturing_variance import (
        DeviceClass,
        ManufacturingVarianceSimulator,
        VarianceModel,
    )


def is_linux() -> bool:
    """Check if running on Linux."""
    return platform.system().lower() == "linux"


def check_linux_requirement(operation: str) -> None:
    """Check if operation requires Linux and raise error if not available."""
    if not is_linux():
        raise RuntimeError(
            f"{operation} requires Linux. "
            f"Current platform: {platform.system()}. "
            f"This functionality is only available on Linux systems."
        )


@dataclass
class RegisterAccess:
    """Represents a single register access event."""

    timestamp: float
    register: str
    offset: int
    operation: str  # 'read' or 'write'
    value: Optional[int] = None
    duration_us: Optional[float] = None


@dataclass
class TimingPattern:
    """Represents a timing pattern in register accesses."""

    pattern_type: str
    registers: List[str]
    avg_interval_us: float
    std_deviation_us: float
    frequency_hz: float
    confidence: float


@dataclass
class BehaviorProfile:
    """Complete behavioral profile of a device."""

    device_bdf: str
    capture_duration: float
    total_accesses: int
    register_accesses: List[RegisterAccess]
    timing_patterns: List[TimingPattern]
    state_transitions: Dict[str, List[str]]
    power_states: List[str]
    interrupt_patterns: Dict[str, Any]
    variance_metadata: Optional[Dict[str, Any]] = None


class BehaviorProfiler:
    """Main class for device behavior profiling."""

    def __init__(self, bdf: str, debug: bool = False, enable_variance: bool = True):
        """
        Initialize the behavior profiler.

        Args:
            bdf: PCIe Bus:Device.Function identifier (e.g., "0000:03:00.0")
            debug: Enable debug logging
            enable_variance: Enable manufacturing variance simulation
        """
        self.bdf = bdf
        self.debug = debug
        self.monitoring = False
        self.access_queue = queue.Queue()
        self.monitor_thread = None

        # Initialize manufacturing variance simulator
        self.enable_variance = enable_variance
        if enable_variance:
            self.variance_simulator = ManufacturingVarianceSimulator()
        else:
            self.variance_simulator = None

        # Validate BDF format
        if not re.match(r"^[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-7]$", bdf):
            raise ValueError(f"Invalid BDF format: {bdf}")

    def _log(self, message: str) -> None:
        """Log debug messages if debug mode is enabled."""
        if self.debug:
            print(f"[BehaviorProfiler] {message}")

    def _setup_monitoring(self) -> bool:
        """
        Set up monitoring infrastructure for the target device.

        Returns:
            True if monitoring setup was successful, False otherwise
        """
        try:
            check_linux_requirement("Device behavior monitoring")

            # Check if device exists
            result = subprocess.run(
                f"lspci -s {self.bdf}", shell=True, capture_output=True, text=True
            )

            if result.returncode != 0 or not result.stdout.strip():
                self._log(f"Device {self.bdf} not found")
                return False

            self._log(f"Found device: {result.stdout.strip()}")

            # Set up ftrace for PCIe config space monitoring (if available)
            self._setup_ftrace()

            return True

        except Exception as e:
            self._log(f"Failed to setup monitoring: {e}")
            return False

    def _setup_ftrace(self) -> None:
        """Set up ftrace for kernel-level monitoring."""
        try:
            # Enable function tracing for PCI config space accesses
            ftrace_cmds = [
                "echo 0 > /sys/kernel/debug/tracing/tracing_on",
                "echo function > /sys/kernel/debug/tracing/current_tracer",
                "echo 'pci_read_config* pci_write_config*' > /sys/kernel/debug/tracing/set_ftrace_filter",
                "echo 1 > /sys/kernel/debug/tracing/tracing_on",
            ]

            for cmd in ftrace_cmds:
                subprocess.run(cmd, shell=True, check=False)

            self._log("Ftrace monitoring enabled")

        except Exception as e:
            self._log(f"Ftrace setup failed (may require root): {e}")

    def _monitor_worker(self) -> None:
        """Worker thread for continuous device monitoring."""
        start_time = time.time()

        while self.monitoring:
            try:
                current_time = time.time()

                # Monitor hardware register accesses through multiple interfaces
                self._monitor_ftrace_events()
                self._monitor_sysfs_accesses()
                self._monitor_debugfs_registers()

                time.sleep(0.001)  # 1ms polling interval

            except Exception as e:
                self._log(f"Monitor worker error: {e}")
                break

    def _monitor_ftrace_events(self) -> None:
        """Monitor register accesses via ftrace events."""
        try:
            # Read ftrace buffer for PCI config space accesses
            trace_path = "/sys/kernel/debug/tracing/trace_pipe"
            if Path(trace_path).exists():
                # Non-blocking read of trace events
                result = subprocess.run(
                    f"timeout 0.001 cat {trace_path}",
                    shell=True,
                    capture_output=True,
                    text=True,
                )

                if result.stdout:
                    self._parse_ftrace_output(result.stdout)

        except (subprocess.TimeoutExpired, PermissionError, FileNotFoundError) as e:
            # Expected errors in non-root environments or when ftrace is unavailable
            self._log(f"Ftrace monitoring unavailable: {e}")
        except Exception as e:
            self._log(f"Ftrace monitoring error: {e}")

    def _monitor_sysfs_accesses(self) -> None:
        """Monitor device state changes via sysfs."""
        try:
            # Monitor device power state and configuration changes
            sysfs_path = f"/sys/bus/pci/devices/{self.bdf}"
            if Path(sysfs_path).exists():
                # Check for configuration space changes
                config_path = f"{sysfs_path}/config"
                if Path(config_path).exists():
                    # Read current configuration state
                    with open(config_path, "rb") as f:
                        config_data = f.read(256)  # Standard config space

                    # Generate access event for configuration reads
                    access = RegisterAccess(
                        timestamp=time.time(),
                        register="CONFIG_SPACE",
                        offset=0x00,
                        operation="read",
                        duration_us=1.0,
                    )
                    self.access_queue.put(access)

        except (PermissionError, FileNotFoundError) as e:
            # Expected in some environments
            self._log(f"Sysfs monitoring limited: {e}")
        except Exception as e:
            self._log(f"Sysfs monitoring error: {e}")

    def _monitor_debugfs_registers(self) -> None:
        """Monitor device registers via debugfs if available."""
        try:
            # Check for device-specific debugfs entries
            debugfs_paths = [
                f"/sys/kernel/debug/pci/{self.bdf}",
                f"/sys/kernel/debug/devices/{self.bdf}",
            ]

            for debug_path in debugfs_paths:
                if Path(debug_path).exists():
                    # Monitor register access patterns
                    self._read_debug_registers(debug_path)
                    break

        except (PermissionError, FileNotFoundError) as e:
            # Expected when debugfs is not available or accessible
            self._log(f"Debugfs monitoring unavailable: {e}")
        except Exception as e:
            self._log(f"Debugfs monitoring error: {e}")

    def _parse_ftrace_output(self, output: str) -> None:
        """Parse ftrace output for PCI access events."""
        try:
            for line in output.splitlines():
                if "pci_read_config" in line or "pci_write_config" in line:
                    # Parse ftrace line format: timestamp function_name args
                    parts = line.strip().split()
                    if len(parts) >= 3:
                        timestamp = (
                            float(parts[0])
                            if parts[0].replace(".", "").isdigit()
                            else time.time()
                        )
                        operation = "read" if "read" in parts[1] else "write"

                        # Extract register offset if available
                        offset = 0
                        for part in parts:
                            if part.startswith("0x") and len(part) <= 6:
                                try:
                                    offset = int(part, 16)
                                    break
                                except ValueError:
                                    continue

                        access = RegisterAccess(
                            timestamp=timestamp,
                            register=f"CONFIG_{offset:03X}",
                            offset=offset,
                            operation=operation,
                            duration_us=2.0,
                        )
                        self.access_queue.put(access)

        except Exception as e:
            self._log(f"Ftrace parsing error: {e}")

    def _read_debug_registers(self, debug_path: str) -> None:
        """Read device registers from debugfs."""
        try:
            # Look for register files in debugfs
            debug_dir = Path(debug_path)
            for reg_file in debug_dir.glob("*reg*"):
                if reg_file.is_file():
                    # Read register value
                    with open(reg_file, "r") as f:
                        content = f.read().strip()

                    # Generate access event
                    access = RegisterAccess(
                        timestamp=time.time(),
                        register=reg_file.name.upper(),
                        offset=0,
                        operation="read",
                        duration_us=1.5,
                    )
                    self.access_queue.put(access)

        except Exception as e:
            self._log(f"Debug register read error: {e}")

    def start_monitoring(self) -> bool:
        """
        Start continuous device monitoring.

        Returns:
            True if monitoring started successfully, False otherwise
        """
        if self.monitoring:
            self._log("Monitoring already active")
            return True

        if not self._setup_monitoring():
            return False

        self.monitoring = True
        self.monitor_thread = threading.Thread(target=self._monitor_worker, daemon=True)
        self.monitor_thread.start()

        self._log("Monitoring started")
        return True

    def stop_monitoring(self) -> None:
        """Stop device monitoring."""
        if not self.monitoring:
            return

        self.monitoring = False
        if self.monitor_thread:
            self.monitor_thread.join(timeout=1.0)

        # Disable ftrace
        try:
            subprocess.run(
                "echo 0 > /sys/kernel/debug/tracing/tracing_on", shell=True, check=False
            )
        except Exception as e:
            # Ignore tracing cleanup errors as they're not critical
            self._log(f"Failed to disable tracing: {e}")

        self._log("Monitoring stopped")

    def capture_behavior_profile(self, duration: float = 30.0) -> BehaviorProfile:
        """
        Capture a complete behavioral profile of the device.

        Args:
            duration: Capture duration in seconds

        Returns:
            BehaviorProfile containing all captured data
        """
        self._log(f"Starting behavior capture for {duration}s")

        if not self.start_monitoring():
            raise RuntimeError("Failed to start monitoring")

        start_time = time.time()
        accesses = []

        try:
            # Collect data for the specified duration
            while time.time() - start_time < duration:
                try:
                    access = self.access_queue.get(timeout=0.1)
                    accesses.append(access)
                except queue.Empty:
                    continue

            # Analyze collected data
            timing_patterns = self._analyze_timing_patterns(accesses)
            state_transitions = self._analyze_state_transitions(accesses)
            interrupt_patterns = self._analyze_interrupt_patterns(accesses)

            profile = BehaviorProfile(
                device_bdf=self.bdf,
                capture_duration=duration,
                total_accesses=len(accesses),
                register_accesses=accesses,
                timing_patterns=timing_patterns,
                state_transitions=state_transitions,
                power_states=["D0"],  # Simplified for demo
                interrupt_patterns=interrupt_patterns,
            )

            self._log(f"Captured {len(accesses)} register accesses")
            return profile

        finally:
            self.stop_monitoring()

    def _analyze_timing_patterns(
        self, accesses: List[RegisterAccess]
    ) -> List[TimingPattern]:
        """Analyze timing patterns in register accesses."""
        patterns = []

        if len(accesses) < 10:
            return patterns

        # Group accesses by register
        reg_accesses = {}
        for access in accesses:
            if access.register not in reg_accesses:
                reg_accesses[access.register] = []
            reg_accesses[access.register].append(access)

        # Analyze timing for each register
        for register, reg_access_list in reg_accesses.items():
            if len(reg_access_list) < 5:
                continue

            # Calculate intervals between accesses
            intervals = []
            for i in range(1, len(reg_access_list)):
                interval = (
                    reg_access_list[i].timestamp - reg_access_list[i - 1].timestamp
                ) * 1000000  # Convert to microseconds
                intervals.append(interval)

            if intervals:
                avg_interval = statistics.mean(intervals)
                std_dev = statistics.stdev(intervals) if len(intervals) > 1 else 0
                frequency = 1000000 / avg_interval if avg_interval > 0 else 0

                # Calculate confidence based on regularity
                confidence = (
                    max(0, 1 - (std_dev / avg_interval)) if avg_interval > 0 else 0
                )

                pattern = TimingPattern(
                    pattern_type="periodic",
                    registers=[register],
                    avg_interval_us=avg_interval,
                    std_deviation_us=std_dev,
                    frequency_hz=frequency,
                    confidence=confidence,
                )
                patterns.append(pattern)

        return patterns

    def _analyze_state_transitions(
        self, accesses: List[RegisterAccess]
    ) -> Dict[str, List[str]]:
        """Analyze state transitions based on register access patterns."""
        transitions = {}

        # Advanced state transition analysis with timing and frequency considerations
        prev_register = None
        prev_timestamp = None
        transition_times = {}
        transition_counts = {}

        # First pass: collect transition data
        for access in accesses:
            if prev_register and prev_register != access.register:
                # Record the transition
                transition_key = (prev_register, access.register)

                # Track transition timing
                if prev_timestamp:
                    transition_time = access.timestamp - prev_timestamp
                    if transition_key not in transition_times:
                        transition_times[transition_key] = []
                    transition_times[transition_key].append(transition_time)

                # Track transition frequency
                if transition_key not in transition_counts:
                    transition_counts[transition_key] = 0
                transition_counts[transition_key] += 1

                # Build the basic transition graph
                if prev_register not in transitions:
                    transitions[prev_register] = []
                if access.register not in transitions[prev_register]:
                    transitions[prev_register].append(access.register)

            prev_register = access.register
            prev_timestamp = access.timestamp

        # Second pass: analyze transition patterns
        # Identify common sequences and potential state machine patterns
        if len(accesses) > 10:  # Only analyze if we have enough data
            # Find repeated sequences (potential state machine cycles)
            register_sequence = [access.register for access in accesses]
            repeated_sequences = self._find_repeated_sequences(register_sequence)

            # Add identified cycles to the transitions with metadata
            for seq in repeated_sequences:
                if len(seq) > 1:
                    cycle_key = "->".join(seq)
                    if "cycles" not in transitions:
                        transitions["cycles"] = {}
                    transitions["cycles"][cycle_key] = {
                        "length": len(seq),
                        "frequency": repeated_sequences[seq],
                    }

        return transitions

    def _find_repeated_sequences(
        self, sequence: List[str], min_length: int = 2, min_occurrences: int = 2
    ) -> Dict[tuple, int]:
        """
        Find repeated subsequences in a list of register accesses.

        Args:
            sequence: List of register names in order of access
            min_length: Minimum length of sequences to consider
            min_occurrences: Minimum number of occurrences to be considered a pattern

        Returns:
            Dictionary mapping sequences (as tuples) to their occurrence count
        """
        sequences = {}
        seq_len = len(sequence)

        # Look for sequences of different lengths
        for length in range(min_length, min(10, seq_len // 2 + 1)):
            # Scan the sequence for patterns of current length
            for i in range(seq_len - length + 1):
                # Extract the subsequence
                subseq = tuple(sequence[i : i + length])

                # Count occurrences
                if subseq not in sequences:
                    # Count non-overlapping occurrences
                    count = 0
                    pos = 0
                    while pos <= seq_len - length:
                        if tuple(sequence[pos : pos + length]) == subseq:
                            count += 1
                            pos += length  # Skip to avoid overlap
                        else:
                            pos += 1

                    if count >= min_occurrences:
                        sequences[subseq] = count

        return sequences

    def _analyze_interrupt_patterns(
        self, accesses: List[RegisterAccess]
    ) -> Dict[str, Any]:
        """Analyze interrupt-related patterns."""
        patterns = {
            "interrupt_registers": [],
            "avg_interrupt_interval_us": 0,
            "interrupt_bursts": [],
        }

        # Look for interrupt-related register accesses
        interrupt_accesses = [
            access
            for access in accesses
            if "irq" in access.register.lower() or "int" in access.register.lower()
        ]

        if interrupt_accesses:
            patterns["interrupt_registers"] = list(
                set(access.register for access in interrupt_accesses)
            )

            if len(interrupt_accesses) > 1:
                intervals = []
                for i in range(1, len(interrupt_accesses)):
                    interval = (
                        interrupt_accesses[i].timestamp
                        - interrupt_accesses[i - 1].timestamp
                    ) * 1000000
                    intervals.append(interval)

                if intervals:
                    patterns["avg_interrupt_interval_us"] = statistics.mean(intervals)

        return patterns

    def analyze_patterns(self, profile: BehaviorProfile) -> Dict[str, Any]:
        """
        Perform advanced pattern analysis on a behavior profile.

        Args:
            profile: BehaviorProfile to analyze

        Returns:
            Dictionary containing analysis results
        """
        analysis = {
            "device_characteristics": {},
            "performance_metrics": {},
            "behavioral_signatures": {},
            "recommendations": [],
        }

        # Device characteristics analysis
        analysis["device_characteristics"] = {
            "total_registers_accessed": len(
                set(access.register for access in profile.register_accesses)
            ),
            "read_write_ratio": self._calculate_rw_ratio(profile.register_accesses),
            "access_frequency_hz": (
                profile.total_accesses / profile.capture_duration
                if profile.capture_duration > 0
                else 0
            ),
            "most_active_registers": self._get_most_active_registers(
                profile.register_accesses, top_n=5
            ),
        }

        # Performance metrics
        access_durations = [
            access.duration_us
            for access in profile.register_accesses
            if access.duration_us
        ]
        if access_durations:
            analysis["performance_metrics"] = {
                "avg_access_duration_us": statistics.mean(access_durations),
                "max_access_duration_us": max(access_durations),
                "min_access_duration_us": min(access_durations),
            }

        # Behavioral signatures
        analysis["behavioral_signatures"] = {
            "timing_regularity": self._calculate_timing_regularity(
                profile.timing_patterns
            ),
            "state_complexity": len(profile.state_transitions),
            "interrupt_activity": len(
                profile.interrupt_patterns.get("interrupt_registers", [])
            ),
        }

        # Manufacturing variance analysis (if enabled)
        if self.enable_variance and self.variance_simulator:
            analysis["variance_analysis"] = self._analyze_manufacturing_variance(
                profile
            )

        # Generate recommendations
        analysis["recommendations"] = self._generate_recommendations(profile, analysis)

        return analysis

    def _calculate_rw_ratio(self, accesses: List[RegisterAccess]) -> float:
        """Calculate read/write ratio."""
        reads = sum(1 for access in accesses if access.operation == "read")
        writes = sum(1 for access in accesses if access.operation == "write")
        return reads / writes if writes > 0 else float("inf")

    def _get_most_active_registers(
        self, accesses: List[RegisterAccess], top_n: int = 5
    ) -> List[Tuple[str, int]]:
        """Get the most frequently accessed registers."""
        reg_counts = {}
        for access in accesses:
            reg_counts[access.register] = reg_counts.get(access.register, 0) + 1

        return sorted(reg_counts.items(), key=lambda x: x[1], reverse=True)[:top_n]

    def _calculate_timing_regularity(self, patterns: List[TimingPattern]) -> float:
        """Calculate overall timing regularity score."""
        if not patterns:
            return 0.0

        return statistics.mean(pattern.confidence for pattern in patterns)

    def _generate_recommendations(
        self, profile: BehaviorProfile, analysis: Dict[str, Any]
    ) -> List[str]:
        """Generate recommendations based on analysis."""
        recommendations = []

        # Check access frequency
        freq = analysis["device_characteristics"]["access_frequency_hz"]
        if freq > 1000:
            recommendations.append(
                "High-frequency device detected - consider optimized timing models"
            )
        elif freq < 10:
            recommendations.append(
                "Low-frequency device - simple polling model may suffice"
            )

        # Check timing regularity
        regularity = analysis["behavioral_signatures"]["timing_regularity"]
        if regularity > 0.8:
            recommendations.append(
                "Highly regular timing patterns - implement precise timing simulation"
            )
        elif regularity < 0.3:
            recommendations.append(
                "Irregular timing patterns - use adaptive timing models"
            )

        # Check interrupt activity
        if analysis["behavioral_signatures"]["interrupt_activity"] > 0:
            recommendations.append(
                "Interrupt-driven device - implement interrupt simulation"
            )

        return recommendations

    def save_profile(self, profile: BehaviorProfile, filepath: str) -> None:
        """Save behavior profile to file."""
        with open(filepath, "w") as f:
            json.dump(asdict(profile), f, indent=2, default=str)

        self._log(f"Profile saved to {filepath}")

    def load_profile(self, filepath: str) -> BehaviorProfile:
        """Load behavior profile from file."""
        with open(filepath, "r") as f:
            data = json.load(f)

        # Convert back to dataclass instances
        accesses = [RegisterAccess(**access) for access in data["register_accesses"]]
        patterns = [TimingPattern(**pattern) for pattern in data["timing_patterns"]]

        profile = BehaviorProfile(
            device_bdf=data["device_bdf"],
            capture_duration=data["capture_duration"],
            total_accesses=data["total_accesses"],
            register_accesses=accesses,
            timing_patterns=patterns,
            state_transitions=data["state_transitions"],
            power_states=data["power_states"],
            interrupt_patterns=data["interrupt_patterns"],
        )

        self._log(f"Profile loaded from {filepath}")
        return profile

    def _analyze_manufacturing_variance(
        self, profile: BehaviorProfile
    ) -> Dict[str, Any]:
        """
        Analyze manufacturing variance patterns in the behavior profile.

        Args:
            profile: BehaviorProfile to analyze for variance patterns

        Returns:
            Dictionary containing variance analysis results
        """
        if not self.variance_simulator:
            return {"variance_enabled": False}

        # Extract timing data for variance analysis
        timing_data = []
        for access in profile.register_accesses:
            if access.duration_us is not None:
                timing_data.append(
                    {
                        "interval_us": access.duration_us,
                        "register": access.register,
                        "operation": access.operation,
                    }
                )

        # Add timing pattern data
        for pattern in profile.timing_patterns:
            timing_data.append(
                {
                    "interval_us": pattern.avg_interval_us,
                    "pattern_type": pattern.pattern_type,
                    "std_deviation_us": pattern.std_deviation_us,
                }
            )

        # Perform variance analysis
        variance_analysis = self.variance_simulator.analyze_timing_patterns(timing_data)

        # Determine appropriate device class based on analysis
        device_class = self._determine_device_class(profile, variance_analysis)

        # Generate variance model for this device
        variance_model = self.variance_simulator.generate_variance_model(
            device_id=profile.device_bdf,
            device_class=device_class,
            base_frequency_mhz=100.0,  # Default 100MHz, could be determined from analysis
        )

        # Get variance metadata
        variance_metadata = self.variance_simulator.get_variance_metadata(
            variance_model
        )

        # Store variance metadata in profile for later use
        profile.variance_metadata = variance_metadata

        return {
            "variance_enabled": True,
            "timing_analysis": variance_analysis,
            "device_class": device_class.value,
            "variance_model": variance_metadata,
            "recommendations": variance_analysis.get("recommendations", []),
        }

    def _determine_device_class(
        self, profile: BehaviorProfile, variance_analysis: Dict[str, Any]
    ) -> DeviceClass:
        """
        Determine the appropriate device class based on behavior profile analysis.

        Args:
            profile: BehaviorProfile to analyze
            variance_analysis: Results from variance analysis

        Returns:
            DeviceClass enum value
        """
        # Calculate access frequency
        access_freq = (
            profile.total_accesses / profile.capture_duration
            if profile.capture_duration > 0
            else 0
        )

        # Get coefficient of variation from variance analysis
        cv = variance_analysis.get("coefficient_of_variation", 0.1)

        # Determine device class based on characteristics
        if access_freq > 10000 and cv < 0.02:
            # High frequency, low variance = enterprise/server grade
            return DeviceClass.ENTERPRISE
        elif access_freq > 1000 and cv < 0.05:
            # Medium-high frequency, low variance = industrial grade
            return DeviceClass.INDUSTRIAL
        elif cv > 0.15:
            # High variance = consumer grade
            return DeviceClass.CONSUMER
        elif "automotive" in profile.device_bdf.lower():
            # BDF suggests automotive context
            return DeviceClass.AUTOMOTIVE
        else:
            # Default to consumer for unknown patterns
            return DeviceClass.CONSUMER


def main():
    """Example usage of the behavior profiler."""
    import argparse

    parser = argparse.ArgumentParser(description="PCIe Device Behavior Profiler")
    parser.add_argument("--bdf", required=True, help="PCIe Bus:Device.Function")
    parser.add_argument(
        "--duration", type=float, default=30.0, help="Capture duration in seconds"
    )
    parser.add_argument("--output", help="Output file for profile data")
    parser.add_argument("--debug", action="store_true", help="Enable debug logging")

    args = parser.parse_args()

    try:
        profiler = BehaviorProfiler(args.bdf, debug=args.debug)
        profile = profiler.capture_behavior_profile(args.duration)
        analysis = profiler.analyze_patterns(profile)

        print(f"Behavior Profile Summary for {args.bdf}:")
        print(f"  Total accesses: {profile.total_accesses}")
        print(f"  Timing patterns: {len(profile.timing_patterns)}")
        print(
            f"  Access frequency: {analysis['device_characteristics']['access_frequency_hz']:.2f} Hz"
        )
        print(
            f"  Timing regularity: {analysis['behavioral_signatures']['timing_regularity']:.2f}"
        )

        print("\nRecommendations:")
        for rec in analysis["recommendations"]:
            print(f"  - {rec}")

        if args.output:
            profiler.save_profile(profile, args.output)
            print(f"\nProfile saved to {args.output}")

    except Exception as e:
        print(f"Error: {e}")
        return 1

    return 0


if __name__ == "__main__":
    exit(main())
