from __future__ import annotations

from dataclasses import dataclass
from enum import IntEnum
from typing import Optional

import torch

from sglang.kernels.ops.kv_canary.verify import RealKvSource


class PoolKind(IntEnum):
    """Which attention regime a canary group belongs to.

    - ``FULL`` covers ``[0, K_req)``. Attached to plain MHA/MLA pools and as one of the two canaries on
      every SWA system.
    - ``SWA`` covers ``[max(0, K_req - window), K_req)``. Attached as the second canary on every
      ``BaseSWAKVPool``.
    """

    FULL = 0
    SWA = 1


@dataclass(frozen=True, slots=True, kw_only=True)
class CanaryBufferGroup:
    """Canary buffers + real-KV sources for one (PoolKind × K-half | V-half) on a pool.

    Each (head | tail) launch sees a single 2-D uint8 buf for the canary, plus a list of RealKvSource for the
    real-KV mixin. Head and tail use separate canary buffers so they can be staged at different points in the
    forward pass without overwriting each other.

    MLA-style pools have no V half (v_head / v_tail = None; real_kv_sources_v is empty). SWA pools have two
    CanaryBufferGroup instances (FULL sized to the full sub-pool, SWA sized to the swa sub-pool).

    Fields:
        kind: PoolKind.FULL or PoolKind.SWA.
        k_head: Head canary buffer for K-half launches, shape [num_slots, CANARY_SLOT_BYTES], uint8.
        k_tail: Tail canary buffer for K-half launches, same shape, uint8.
        v_head: Same for V-half, or None for MLA-style pools.
        v_tail: Same for V-half, or None.
        real_kv_sources_k: Real KV pieces folded into the K-half canary's real_kv_hash. Tuple length is
            pool-specific (1 for simple MHA, more for multi-layer / weird-layout pools). Empty tuple =
            real-KV mixin disabled for this half.
        real_kv_sources_v: Same for V-half. Empty tuple iff v_head is None or the mixin is disabled.
        swa_index_lut: SWA full-to-swa index mapping LUT, shape [full_pool_size + 1], int64, or None for FULL
            groups. Used by launch_canary_plan_kernels to translate verify/seed slot indices at plan time, and by
            launch_canary_write_kernel to translate write slots inline. None iff kind == PoolKind.FULL.
        kv_token_id_vs_position_offset: Logical-position offset between a canary slot and the source-of-truth token it
            fingerprints. 0 for target-style pools (slot ``p`` stores K/V for token at position ``p``); 1 for
            EAGLE draft pools where the input_ids rotation makes slot ``p`` store K/V for token at position
            ``p + 1``.
    """

    kind: PoolKind
    k_head: torch.Tensor
    k_tail: torch.Tensor
    v_head: Optional[torch.Tensor]
    v_tail: Optional[torch.Tensor]
    real_kv_sources_k: tuple[RealKvSource, ...]
    real_kv_sources_v: tuple[RealKvSource, ...]
    swa_index_lut: Optional[torch.Tensor]
    kv_token_id_vs_position_offset: int

    @property
    def has_v_half(self) -> bool:
        return self.v_head is not None
