from __future__ import annotations

import pytest

from leshy.buffs.encoding import (
    Base64Buff,
    HexBuff,
    LeetspeakBuff,
    LowercaseBuff,
    PrefixBuff,
    ReverseBuff,
    Rot13Buff,
    SuffixBuff,
    UppercaseBuff,
    WrapBuff,
)


class TestEncodingBuffs:
    @pytest.mark.asyncio
    async def test_base64(self) -> None:
        buff = Base64Buff()
        result = await buff.transform("hello")
        assert result == "aGVsbG8="

    @pytest.mark.asyncio
    async def test_rot13(self) -> None:
        buff = Rot13Buff()
        result = await buff.transform("hello")
        assert result == "uryyb"
        # ROT13 is self-inverse
        result2 = await buff.transform(result)
        assert result2 == "hello"

    @pytest.mark.asyncio
    async def test_leetspeak(self) -> None:
        buff = LeetspeakBuff()
        result = await buff.transform("elite")
        assert result == "31173"  # e->3, l->1, i->1, t->7, e->3

    @pytest.mark.asyncio
    async def test_reverse(self) -> None:
        buff = ReverseBuff()
        result = await buff.transform("hello")
        assert result == "olleh"

    @pytest.mark.asyncio
    async def test_uppercase(self) -> None:
        buff = UppercaseBuff()
        result = await buff.transform("Hello World")
        assert result == "HELLO WORLD"

    @pytest.mark.asyncio
    async def test_lowercase(self) -> None:
        buff = LowercaseBuff()
        result = await buff.transform("Hello World")
        assert result == "hello world"

    @pytest.mark.asyncio
    async def test_hex(self) -> None:
        buff = HexBuff()
        result = await buff.transform("hi")
        assert result == "6869"

    @pytest.mark.asyncio
    async def test_prefix(self) -> None:
        buff = PrefixBuff(text="[START] ")
        result = await buff.transform("payload")
        assert result == "[START] payload"

    @pytest.mark.asyncio
    async def test_suffix(self) -> None:
        buff = SuffixBuff(text=" [END]")
        result = await buff.transform("payload")
        assert result == "payload [END]"

    @pytest.mark.asyncio
    async def test_wrap(self) -> None:
        buff = WrapBuff(prefix="<<", suffix=">>")
        result = await buff.transform("content")
        assert result == "<<content>>"


class TestBuffParamValidation:
    def test_prefix_requires_text(self) -> None:
        with pytest.raises(ValueError, match="requires a 'text' parameter"):
            PrefixBuff()

    def test_suffix_requires_text(self) -> None:
        with pytest.raises(ValueError, match="requires a 'text' parameter"):
            SuffixBuff()

    def test_wrap_requires_at_least_one_param(self) -> None:
        with pytest.raises(ValueError, match="requires at least one"):
            WrapBuff()

    def test_wrap_accepts_prefix_only(self) -> None:
        buff = WrapBuff(prefix="<<")
        assert buff._prefix == "<<"
        assert buff._suffix == ""

    def test_wrap_accepts_suffix_only(self) -> None:
        buff = WrapBuff(suffix=">>")
        assert buff._prefix == ""
        assert buff._suffix == ">>"


class TestBuffChaining:
    @pytest.mark.asyncio
    async def test_multiple_buffs_in_sequence(self) -> None:
        # Simulate what the runner does
        content = "test"
        buffs = [UppercaseBuff(), ReverseBuff()]
        for buff in buffs:
            content = await buff.transform(content)
        assert content == "TSET"

    @pytest.mark.asyncio
    async def test_encode_then_wrap(self) -> None:
        content = "secret"
        buffs = [Base64Buff(), WrapBuff(prefix="decode this: ", suffix=" please")]
        for buff in buffs:
            content = await buff.transform(content)
        assert content == "decode this: c2VjcmV0 please"
