"""Shared concurrency parsing and auto-scaling policy."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Iterable

from worai.errors import UsageError


@dataclass(frozen=True)
class ConcurrencySettings:
    mode: str
    workers: int
    min_workers: int
    max_workers: int

    @property
    def is_auto(self) -> bool:
        return self.mode == "auto"

    def with_workers(self, workers: int) -> ConcurrencySettings:
        if self.is_auto:
            bounded = max(self.min_workers, min(workers, self.max_workers))
            return ConcurrencySettings(
                mode=self.mode,
                workers=bounded,
                min_workers=self.min_workers,
                max_workers=self.max_workers,
            )
        return self

    def next_workers(self, buckets: Iterable[str]) -> int:
        if not self.is_auto:
            return self.workers

        bucket_set = set(buckets)
        if bucket_set & {"throttle", "server_error", "error"}:
            return max(self.min_workers, self.workers - 1)
        if bucket_set and bucket_set == {"ok"}:
            return min(self.max_workers, self.workers + 1)
        return self.workers


def parse_concurrency(
    value: str | int,
    *,
    auto_default_workers: int = 4,
    auto_min_workers: int = 2,
    auto_max_workers: int = 12,
) -> ConcurrencySettings:
    if isinstance(value, int):
        workers = value
    else:
        normalized = value.strip().lower()
        if normalized == "auto":
            workers = max(auto_min_workers, min(auto_default_workers, auto_max_workers))
            return ConcurrencySettings(
                mode="auto",
                workers=workers,
                min_workers=auto_min_workers,
                max_workers=auto_max_workers,
            )
        try:
            workers = int(normalized)
        except ValueError as exc:
            raise UsageError("Concurrency must be an integer or 'auto'.") from exc

    if workers <= 0:
        raise UsageError("Concurrency must be greater than 0.")

    return ConcurrencySettings(
        mode="fixed",
        workers=workers,
        min_workers=workers,
        max_workers=workers,
    )


def classify_status_bucket(status_code: int | None, *, had_error: bool = False) -> str:
    if had_error:
        return "error"
    if status_code is None:
        return "error"
    if status_code == 429:
        return "throttle"
    if 500 <= status_code < 600:
        return "server_error"
    if 200 <= status_code < 400:
        return "ok"
    return "client_error"
