"""PE_TCM: tightly-coupled memory with BW-based access serialization (ADR-0014 D1).

Models scratchpad memory inside the PE. Handles both legacy Transaction forwarding
and TcmRequest from PE_FETCH_STORE for BW-serialized read/write access.

Two channels (read/write) with independent serialization.
Ported from pe_accel TcmBlock timing model.
"""
from __future__ import annotations

from collections.abc import Generator
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any

import simpy

from kernbench.components.base import ComponentBase

if TYPE_CHECKING:
    from kernbench.components.context import ComponentContext
    from kernbench.topology.types import Node


@dataclass
class TcmRequest:
    """Request to read from or write to TCM (used by PE_FETCH_STORE)."""

    direction: str      # "read" or "write"
    nbytes: int
    done: simpy.Event
    tag: str = ""


class PeTcmComponent(ComponentBase):
    """PE_TCM: BW-serialized scratchpad memory (ADR-0014 D1).

    Dual-channel: read and write can proceed in parallel,
    but concurrent reads serialize, concurrent writes serialize.
    BW from topology attrs or pe_template links.
    """

    def __init__(self, node: Node, ctx: ComponentContext | None = None) -> None:
        super().__init__(node, ctx)
        self._read_bw: float = float(node.attrs.get("read_bw_gbs", 512.0))
        self._write_bw: float = float(node.attrs.get("write_bw_gbs", 512.0))
        self._read_res: simpy.Resource | None = None
        self._write_res: simpy.Resource | None = None

    def start(self, env: simpy.Environment) -> None:
        self._read_res = simpy.Resource(env, capacity=1)
        self._write_res = simpy.Resource(env, capacity=1)
        super().start(env)

    def run(self, env: simpy.Environment, nbytes: int) -> Generator:
        overhead_ns = float(self.node.attrs.get("overhead_ns", 0.0))
        yield env.timeout(overhead_ns)

    def _worker(self, env: simpy.Environment) -> Generator:
        """Dispatch TcmRequest (from fetch_store) and Transaction (fabric)."""
        while True:
            msg: Any = yield self._inbox.get()
            if isinstance(msg, TcmRequest):
                env.process(self._handle_tcm_request(env, msg))
            else:
                env.process(self._forward_txn(env, msg))

    def _handle_tcm_request(self, env: simpy.Environment, req: TcmRequest) -> Generator:
        """BW-serialized access: acquire channel, apply delay, signal done."""
        if req.direction == "write":
            res = self._write_res
            bw = self._write_bw
        else:
            res = self._read_res
            bw = self._read_bw

        assert res is not None
        with res.request() as lock:
            yield lock
            if bw > 0 and req.nbytes > 0:
                delay_ns = req.nbytes / bw
                yield env.timeout(delay_ns)
            req.done.succeed()
