from __future__ import annotations

import random
from collections.abc import Sequence
from typing import Any, cast

from glitchlings.constants import DEFAULT_ZEEDUB_RATE, ZEEDUB_DEFAULT_ZERO_WIDTHS
from glitchlings.internal.rust_ffi import (
    inject_zero_widths_rust,
    resolve_seed,
)

from .core import AttackOrder, AttackWave, Glitchling, PipelineOperationPayload

_DEFAULT_ZERO_WIDTH_CHARACTERS: tuple[str, ...] = ZEEDUB_DEFAULT_ZERO_WIDTHS


def insert_zero_widths(
    text: str,
    rate: float | None = None,
    seed: int | None = None,
    rng: random.Random | None = None,
    *,
    characters: Sequence[str] | None = None,
) -> str:
    """Inject zero-width characters between non-space character pairs."""
    effective_rate = DEFAULT_ZEEDUB_RATE if rate is None else rate

    palette: Sequence[str] = (
        tuple(characters) if characters is not None else ZEEDUB_DEFAULT_ZERO_WIDTHS
    )

    cleaned_palette = tuple(char for char in palette if char)
    if not cleaned_palette or not text:
        return text

    clamped_rate = max(0.0, effective_rate)
    if clamped_rate == 0.0:
        return text

    seed_value = resolve_seed(seed, rng)
    return inject_zero_widths_rust(text, clamped_rate, list(cleaned_palette), seed_value)


class Zeedub(Glitchling):
    """Glitchling that plants zero-width glyphs inside words."""

    flavor = "I'm invoking my right to remain silent."

    def __init__(
        self,
        *,
        rate: float | None = None,
        seed: int | None = None,
        characters: Sequence[str] | None = None,
        **kwargs: Any,
    ) -> None:
        effective_rate = DEFAULT_ZEEDUB_RATE if rate is None else rate
        super().__init__(
            name="Zeedub",
            corruption_function=insert_zero_widths,
            scope=AttackWave.CHARACTER,
            order=AttackOrder.LAST,
            seed=seed,
            rate=effective_rate,
            characters=tuple(characters) if characters is not None else None,
            **kwargs,
        )

    def pipeline_operation(self) -> PipelineOperationPayload:
        rate = float(self.kwargs.get("rate", DEFAULT_ZEEDUB_RATE))

        raw_characters = self.kwargs.get("characters")
        palette = (
            tuple(ZEEDUB_DEFAULT_ZERO_WIDTHS)
            if raw_characters is None
            else tuple(str(char) for char in raw_characters if char)
        )

        return cast(
            PipelineOperationPayload,
            {
                "type": "zwj",
                "rate": rate,
                "characters": list(palette),
            },
        )


zeedub = Zeedub()


__all__ = ["Zeedub", "zeedub", "insert_zero_widths"]
