"""Sequence number field analysis."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    from oscura.analyzers.binary.core.results import Field, Message


class SequenceAnalyzer:
    """Analyze sequence number fields.

    Detects monotonic counters, wrapping behavior, gaps, and duplicates.

    Example:
        >>> analyzer = SequenceAnalyzer()
        >>> metadata = analyzer.analyze(field, messages)
        >>> if metadata["is_sequence"]:
        ...     print(f"Sequence: {metadata['start']} + {metadata['increment']}")
    """

    def analyze(self, field: Field, messages: list[Message]) -> dict[str, Any]:
        """Analyze sequence number field.

        Args:
            field: Field to analyze.
            messages: List of messages containing field.

        Returns:
            Metadata dict with sequence information.

        Example:
            >>> result = analyzer.analyze(field, messages)
            >>> result["is_sequence"]  # True if monotonic
            >>> result["gaps"]  # List of missing values
        """
        if len(messages) < 3:
            return {
                "is_sequence": False,
                "reason": "insufficient_data",
            }

        # Extract field values from all messages
        values = self._extract_values(field, messages)

        if len(values) < 3:
            return {
                "is_sequence": False,
                "reason": "could_not_extract_values",
            }

        # Check if monotonic increasing
        is_monotonic = self._is_monotonic_increasing(values)

        if not is_monotonic:
            return {
                "is_sequence": False,
                "reason": "not_monotonic",
            }

        # Analyze sequence characteristics
        start_value = int(values[0])
        increment = self._detect_increment(values)
        gaps = self._detect_gaps(values, increment)
        duplicates = self._detect_duplicates(values)
        wraps = self._detect_wrapping(values, field.length)

        return {
            "is_sequence": True,
            "start_value": start_value,
            "end_value": int(values[-1]),
            "increment": increment,
            "total_values": len(values),
            "unique_values": len(set(values)),
            "gaps": gaps,
            "duplicates": duplicates,
            "wrapping_detected": wraps > 0,
            "wrap_count": wraps,
        }

    def _extract_values(self, field: Field, messages: list[Message]) -> list[int]:
        """Extract field values from messages.

        Args:
            field: Field specification.
            messages: List of messages.

        Returns:
            List of integer values.
        """
        values = []

        for msg in messages:
            if field.offset + field.length <= len(msg.data):
                field_bytes = msg.data[field.offset : field.offset + field.length]

                try:
                    # Try interpreting as different integer types
                    if field.length == 1:
                        value = int(field_bytes[0])
                    elif field.length == 2 or field.length == 4 or field.length == 8:
                        value = int.from_bytes(field_bytes, byteorder="little", signed=False)
                    else:
                        continue

                    values.append(value)
                except Exception:
                    continue

        return values

    def _is_monotonic_increasing(self, values: list[int]) -> bool:
        """Check if values are monotonically increasing (allowing wraps).

        Args:
            values: List of values.

        Returns:
            True if mostly increasing.
        """
        if len(values) < 2:
            return False

        increasing_count = 0
        total_pairs = 0

        for i in range(len(values) - 1):
            diff = values[i + 1] - values[i]

            # Allow small decreases for wrapping
            if diff >= 0 or diff < -1000:  # Likely wrapped
                increasing_count += 1

            total_pairs += 1

        # Require 95% increasing
        return increasing_count / total_pairs > 0.95

    def _detect_increment(self, values: list[int]) -> int:
        """Detect common increment value.

        Args:
            values: List of values.

        Returns:
            Most common increment.
        """
        if len(values) < 2:
            return 1

        diffs = [values[i + 1] - values[i] for i in range(len(values) - 1)]

        # Filter out negative diffs (wraps)
        positive_diffs = [d for d in diffs if d > 0 and d < 1000]

        if not positive_diffs:
            return 1

        # Find most common diff
        from collections import Counter

        counter = Counter(positive_diffs)
        return counter.most_common(1)[0][0]

    def _detect_gaps(self, values: list[int], increment: int) -> list[int]:
        """Detect missing values in sequence.

        Args:
            values: List of values.
            increment: Expected increment.

        Returns:
            List of missing values.
        """
        gaps = []

        for i in range(len(values) - 1):
            expected_next = values[i] + increment
            actual_next = values[i + 1]

            if actual_next != expected_next and actual_next > expected_next:
                # There's a gap
                for missing in range(expected_next, actual_next, increment):
                    gaps.append(missing)

        return gaps[:100]  # Limit to first 100 gaps

    def _detect_duplicates(self, values: list[int]) -> list[int]:
        """Detect duplicate values.

        Args:
            values: List of values.

        Returns:
            List of duplicate values.
        """
        from collections import Counter

        counter = Counter(values)
        duplicates = [val for val, count in counter.items() if count > 1]

        return duplicates[:100]  # Limit to first 100

    def _detect_wrapping(self, values: list[int], field_length: int) -> int:
        """Detect counter wrapping.

        Args:
            values: List of values.
            field_length: Field length in bytes.

        Returns:
            Number of detected wraps.
        """
        # Determine max value for field length
        if field_length == 1:
            max_val = 255
        elif field_length == 2:
            max_val = 65535
        elif field_length == 4:
            max_val = 4294967295
        else:
            return 0

        wrap_count = 0

        for i in range(len(values) - 1):
            # Detect wrap: value decreases significantly
            if values[i] > values[i + 1] and values[i] > max_val * 0.9:
                wrap_count += 1

        return wrap_count
