"""Checksum field analysis."""

from __future__ import annotations

import zlib
from typing import TYPE_CHECKING, Any

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


class ChecksumAnalyzer:
    """Analyze checksum fields.

    Tests various checksum algorithms and validates against message data.

    Example:
        >>> analyzer = ChecksumAnalyzer()
        >>> metadata = analyzer.analyze(field, messages)
        >>> if metadata["algorithm"] != "unknown":
        ...     print(f"Algorithm: {metadata['algorithm']}")
        ...     print(f"Match rate: {metadata['match_rate']:.1%}")
    """

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

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

        Returns:
            Metadata dict with checksum information.

        Example:
            >>> result = analyzer.analyze(field, messages)
            >>> result["algorithm"]  # e.g., "crc32"
            >>> result["match_rate"]  # e.g., 0.997
        """
        if len(messages) < 5:
            return {
                "algorithm": "unknown",
                "reason": "insufficient_data",
                "match_rate": 0.0,
            }

        # Test various algorithms
        algorithms = []

        if field.length == 1:
            algorithms = ["sum8", "xor8"]
        elif field.length == 2:
            algorithms = ["crc16", "fletcher16", "sum16"]
        elif field.length == 4:
            algorithms = ["crc32", "adler32", "sum32"]
        else:
            return {
                "algorithm": "unknown",
                "reason": "unsupported_length",
                "match_rate": 0.0,
            }

        best_algorithm = "unknown"
        best_match_rate = 0.0
        best_matches = 0

        for algorithm in algorithms:
            match_rate, matches = self._test_algorithm(algorithm, field, messages)

            if match_rate > best_match_rate:
                best_match_rate = match_rate
                best_algorithm = algorithm
                best_matches = matches

        # Require >95% match rate for confirmation
        if best_match_rate < 0.95:
            return {
                "algorithm": "unknown",
                "reason": "low_match_rate",
                "match_rate": best_match_rate,
                "tested_algorithms": algorithms,
            }

        return {
            "algorithm": best_algorithm,
            "match_rate": best_match_rate,
            "validated_count": best_matches,
            "total_messages": len(messages),
            "confidence": min(1.0, best_match_rate),
        }

    def _test_algorithm(
        self, algorithm: str, field: Field, messages: list[Message]
    ) -> tuple[float, int]:
        """Test a checksum algorithm against messages.

        Args:
            algorithm: Algorithm name to test.
            field: Checksum field.
            messages: List of messages.

        Returns:
            (match_rate, match_count) tuple.
        """
        matches = 0
        tested = 0

        for msg in messages[:100]:  # Test first 100 messages
            if field.offset + field.length > len(msg.data):
                continue

            # Extract expected checksum from field
            expected_bytes = msg.data[field.offset : field.offset + field.length]
            expected = int.from_bytes(expected_bytes, byteorder="little", signed=False)

            # Calculate checksum over payload (excluding checksum field)
            # Try checksumming data before and after the field
            payload_before = msg.data[: field.offset]
            payload_after = msg.data[field.offset + field.length :]

            # Most common: checksum covers everything except itself
            payload = payload_before + payload_after

            # Calculate checksum
            calculated = self._calculate_checksum(algorithm, payload)

            if calculated == expected:
                matches += 1

            tested += 1

        match_rate = matches / tested if tested > 0 else 0.0
        return match_rate, matches

    def _calculate_checksum(self, algorithm: str, data: bytes) -> int:
        """Calculate checksum using specified algorithm.

        Args:
            algorithm: Algorithm name.
            data: Data to checksum.

        Returns:
            Checksum value.
        """
        if algorithm == "crc32":
            return zlib.crc32(data) & 0xFFFFFFFF

        elif algorithm == "adler32":
            return zlib.adler32(data) & 0xFFFFFFFF

        elif algorithm == "crc16":
            return self._crc16(data)

        elif algorithm == "fletcher16":
            return self._fletcher16(data)

        elif algorithm == "sum8":
            return sum(data) & 0xFF

        elif algorithm == "xor8":
            result = 0
            for b in data:
                result ^= b
            return result & 0xFF

        elif algorithm == "sum16":
            return sum(data) & 0xFFFF

        elif algorithm == "sum32":
            return sum(data) & 0xFFFFFFFF

        return 0

    def _crc16(self, data: bytes) -> int:
        """Calculate CRC-16 (CCITT).

        Args:
            data: Input data.

        Returns:
            CRC-16 value.
        """
        crc = 0xFFFF

        for byte in data:
            crc ^= byte << 8

            for _ in range(8):
                if crc & 0x8000:
                    crc = (crc << 1) ^ 0x1021
                else:
                    crc = crc << 1

                crc &= 0xFFFF

        return crc

    def _fletcher16(self, data: bytes) -> int:
        """Calculate Fletcher-16 checksum.

        Args:
            data: Input data.

        Returns:
            Fletcher-16 value.
        """
        sum1 = 0
        sum2 = 0

        for byte in data:
            sum1 = (sum1 + byte) % 255
            sum2 = (sum2 + sum1) % 255

        return (sum2 << 8) | sum1
