# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass
from typing import TYPE_CHECKING, cast

import pytest

from weakincentives.prompt._visibility import (
    SectionVisibility,
    _visibility_requires_positional_argument,
    normalize_visibility_selector,
)
from weakincentives.runtime.events import InProcessDispatcher
from weakincentives.runtime.session import Session

if TYPE_CHECKING:
    from weakincentives.runtime.session.protocols import SessionProtocol


@dataclass
class _VisibilityParams:
    flag: bool = False


def test_visibility_selector_rejects_invalid_return_type() -> None:
    selector = normalize_visibility_selector(
        lambda params: cast(SectionVisibility, "not-visibility"), _VisibilityParams
    )

    with pytest.raises(TypeError):
        selector(_VisibilityParams(), None)


def test_visibility_requires_positional_argument_branches() -> None:
    assert (
        _visibility_requires_positional_argument(lambda: SectionVisibility.FULL)
        is False
    )
    assert (
        _visibility_requires_positional_argument(lambda params: SectionVisibility.FULL)
        is True
    )
    assert (
        _visibility_requires_positional_argument(
            cast(Callable[..., SectionVisibility], object())
        )
        is True
    )


def test_normalize_visibility_selector_with_session_kwarg() -> None:
    dispatcher = InProcessDispatcher()
    session = Session(dispatcher=dispatcher)

    received_session: list[SessionProtocol | None] = []

    def selector(*, session: SessionProtocol | None) -> SectionVisibility:
        received_session.append(session)
        return (
            SectionVisibility.FULL if session is not None else SectionVisibility.SUMMARY
        )

    normalized = normalize_visibility_selector(selector, params_type=None)

    assert normalized(None, session) == SectionVisibility.FULL
    assert received_session == [session]
    assert normalized(None, None) == SectionVisibility.SUMMARY


def test_normalize_visibility_selector_with_params_and_session() -> None:
    dispatcher = InProcessDispatcher()
    session = Session(dispatcher=dispatcher)

    received: list[tuple[_VisibilityParams, SessionProtocol | None]] = []

    def selector(
        params: _VisibilityParams, *, session: SessionProtocol | None
    ) -> SectionVisibility:
        received.append((params, session))
        return SectionVisibility.FULL if params.flag else SectionVisibility.SUMMARY

    normalized = normalize_visibility_selector(selector, params_type=_VisibilityParams)

    params = _VisibilityParams(flag=True)
    assert normalized(params, session) == SectionVisibility.FULL
    assert received == [(params, session)]

    params_false = _VisibilityParams(flag=False)
    assert normalized(params_false, session) == SectionVisibility.SUMMARY


def test_normalize_visibility_selector_constant() -> None:
    """Constant visibility selectors should work with the new signature."""
    selector = normalize_visibility_selector(
        SectionVisibility.SUMMARY, params_type=None
    )

    dispatcher = InProcessDispatcher()
    session = Session(dispatcher=dispatcher)

    assert selector(None, None) == SectionVisibility.SUMMARY
    assert selector(None, session) == SectionVisibility.SUMMARY
