from __future__ import annotations

from dataclasses import dataclass

from prompt_toolkit.layout.containers import FloatContainer, Window

from pulka.tui.modal_manager import ModalManager


def test_modal_dimensions_respect_terminal_size_and_chrome() -> None:
    width, height = ModalManager.calculate_dimensions_for_size(
        target_width=120,
        target_height=40,
        columns=100,
        rows=20,
        chrome_height=8,
    )
    assert width == 80  # 80% of 100
    assert height >= 11  # 3 + chrome height
    assert height <= 20


def test_modal_dimensions_use_target_when_terminal_large_enough() -> None:
    width, height = ModalManager.calculate_dimensions_for_size(
        target_width=90,
        target_height=20,
        columns=200,
        rows=60,
        chrome_height=8,
    )
    assert width == 90
    assert height == 20


@dataclass
class _Size:
    columns: int = 120
    rows: int = 40


class _Output:
    def get_size(self) -> _Size:
        return _Size()


class _Renderer:
    def __init__(self) -> None:
        self.reset_calls = 0

    def reset(self) -> None:
        self.reset_calls += 1


class _Layout:
    def __init__(self, *, table_window: Window) -> None:
        self._table_window = table_window
        self.focus_calls: list[object] = []

    def focus(self, target: object) -> None:
        self.focus_calls.append(target)

    def find_all_windows(self) -> list[Window]:
        return [self._table_window]


class _App:
    def __init__(self, *, table_window: Window) -> None:
        self.output = _Output()
        self.renderer = _Renderer()
        self.layout = _Layout(table_window=table_window)
        self.invalidate_calls = 0

    def invalidate(self) -> None:
        self.invalidate_calls += 1


def test_remove_resets_renderer_to_clear_modal_artifacts() -> None:
    table_window = Window()
    window = FloatContainer(content=Window(), floats=[])
    manager = ModalManager(window=window, table_window=table_window)
    app = _App(table_window=table_window)

    # Establish a modal so the remove path executes its cleanup logic.
    manager.display(app, Window())
    assert manager.active

    manager.remove(app)

    assert not manager.active
    assert app.renderer.reset_calls == 1
    assert app.invalidate_calls >= 2
