from __future__ import annotations

from dataclasses import dataclass, field
from typing import Dict, List, Literal, Optional, Sequence, Union

WyndDataIngestPayloadMode = Literal["ndjson"]
WyndDataIngestTableOperation = Literal["insert", "new", "update", "delete"]
WyndDataIngestPayload = Union[Sequence[object], bytes, bytearray]
HeaderValue = Union[str, List[str]]


@dataclass(slots=True)
class RetryOptions:
    max_attempts: int = 5
    initial_delay_ms: int = 200
    max_delay_ms: int = 5_000
    factor: float = 2.0
    jitter_ratio: float = 0.2
    retryable_status_codes: Sequence[int] = field(
        default_factory=lambda: (408, 425, 429, 500, 502, 503, 504)
    )


@dataclass(slots=True)
class GzipCompressionOptions:
    min_bytes: int
    level: Optional[int] = None


@dataclass(slots=True)
class CompressionOptions:
    gzip: Union[bool, GzipCompressionOptions, None] = None


@dataclass(slots=True)
class BatchingOptions:
    linger_ms: int = 1_000
    max_records: int = 500
    max_bytes: int = 5 * 1_024 * 1_024
    max_inflight_batches: int = 4


@dataclass(slots=True)
class SendOptions:
    headers: Optional[Dict[str, str]] = None
    content_type: Optional[str] = None
    timeout_ms: Optional[int] = None


@dataclass(slots=True)
class WyndDataIngestRequestResult:
    status_code: int
    ok: bool
    headers: Dict[str, HeaderValue]
    attempts: int
    body_text: Optional[str] = None


class WyndDataIngestError(Exception):
    def __init__(
        self,
        message: str,
        *,
        attempts: int,
        status_code: Optional[int] = None,
        response_body: Optional[str] = None,
        cause: Optional[BaseException] = None,
    ) -> None:
        super().__init__(message)
        self.attempts = attempts
        self.status_code = status_code
        self.response_body = response_body
        self.cause = cause
