"""Dead letter queue routing for failed records."""

from __future__ import annotations

import time
from typing import Optional, Protocol

from neonlink.errors import classify
from neonlink.record import HEADER_RETRY_COUNT, Record


class DLQPublisher(Protocol):
    """Interface for routing records to the dead letter queue.

    Both Producer and MockProducer satisfy this protocol.
    """

    def publish(
        self,
        topic: str,
        key: Optional[bytes],
        value: Optional[bytes],
        headers: Optional[dict[str, str | bytes]] = None,
    ) -> None: ...


def route_to_dlq(
    dlq_producer: DLQPublisher,
    dlq_topic: str,
    original: Record,
    processing_error: Exception,
    poison_reason: str = "",
    retry_count: int = 0,
    consumer_group: str = "",
) -> None:
    """Publish a failed record to the DLQ topic with enriched error metadata.

    The original record's headers, key, and value are preserved.
    Additional DLQ forensics headers are added:
      - dlq_original_topic, dlq_original_partition, dlq_original_offset
      - dlq_error, dlq_timestamp, dlq_reason
      - dlq_retry_count, dlq_poison_reason, dlq_consumer_group, dlq_error_class
    """
    dlq_headers: dict[str, str | bytes] = dict(original.headers)
    dlq_headers["dlq_original_topic"] = original.topic
    dlq_headers["dlq_original_partition"] = str(original.partition)
    dlq_headers["dlq_original_offset"] = str(original.offset)
    dlq_headers["dlq_error"] = str(processing_error)
    dlq_headers["dlq_timestamp"] = time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime())
    dlq_headers["dlq_reason"] = _classify_reason(original)

    # Enriched forensics headers.
    dlq_headers["dlq_retry_count"] = str(retry_count)
    dlq_headers["dlq_poison_reason"] = poison_reason or _classify_reason(original)
    if consumer_group:
        dlq_headers["dlq_consumer_group"] = consumer_group
    dlq_headers["dlq_error_class"] = classify(processing_error).value

    dlq_producer.publish(dlq_topic, original.key, original.value, dlq_headers)


def _classify_reason(rec: Record) -> str:
    """Return a human-readable reason for DLQ routing."""
    retry_str = rec.get_header(HEADER_RETRY_COUNT)
    if retry_str:
        try:
            count = int(retry_str)
            if count > 0:
                return f"MAX_RETRIES_EXCEEDED(retry_count={count})"
        except ValueError:
            pass
    return "NON_RETRIABLE_ERROR"
