"""Findings panel rendering tests.

``render_findings_panel`` is the central widget on the home page and
the Review page; failures here cascade into the user's first
impression. We drive it via a tiny test harness page
(``_findings_panel_harness.py``) so the test can inject findings
directly into session state — no file_uploader simulation needed.

We verify:

- Empty findings list → localized "no issues" success message.
- Findings with tool ids → one expander per tool, labeled in the
  active language.
- Header + severity summary render at the top.
- Untargeted findings land in the "Other / file-level" expander.

Pack-key parity is already pinned by ``test_lang_packs.py``; this
file pins the call sites instead.
"""

from __future__ import annotations

from pathlib import Path

import pandas as pd
import pytest

from streamlit.testing.v1 import AppTest

from .conftest import PROJECT_ROOT, collected_text, with_language

HARNESS_PATH = Path(__file__).resolve().parent / "_findings_panel_harness.py"


def _harness(findings, lang: str = "en") -> AppTest:
    """Build an AppTest of the harness page with ``findings`` pre-stashed."""
    app = AppTest.from_file(str(HARNESS_PATH))
    app.session_state["test_findings"] = findings
    if lang != "en":
        app.session_state["ui_lang"] = lang
    return app


def _make_finding(tool: str = "", **overrides):
    """Build a minimal :class:`Finding` object. ``Finding`` is a frozen
    dataclass; constructor signature is well-pinned by core tests, so
    we use it directly here rather than building dicts."""
    from src.core.analyze import Finding
    kwargs = dict(
        id="test_finding",
        severity="warn",
        tool=tool,
        count=1,
        description="A test finding.",
        column=None,
        samples=[],
        confidence="medium",
        fix_action="",
    )
    kwargs.update(overrides)
    return Finding(**kwargs)


# ---------------------------------------------------------------------------
# Empty findings → success message
# ---------------------------------------------------------------------------

class TestEmptyFindings:
    def test_empty_renders_no_issues_english(self):
        app = _harness([])
        app.run()
        text = collected_text(app)
        assert "No issues detected" in text

    def test_empty_renders_no_issues_spanish(self):
        app = _harness([], lang="es")
        app.run()
        text = collected_text(app)
        assert "No se detectaron problemas" in text


# ---------------------------------------------------------------------------
# Header text
# ---------------------------------------------------------------------------

class TestHeader:
    def test_header_english(self):
        app = _harness([_make_finding(tool="02_text_cleaner")])
        app.run()
        text = collected_text(app)
        assert "Detected issues" in text

    def test_header_spanish(self):
        app = _harness([_make_finding(tool="02_text_cleaner")], lang="es")
        app.run()
        text = collected_text(app)
        assert "Problemas detectados" in text


# ---------------------------------------------------------------------------
# Per-finding row → one "Open Tool" button per targeted finding
# ---------------------------------------------------------------------------
#
# The findings panel was redesigned (mockup-v2): it now renders ONE
# severity-sorted flat list rather than per-tool expanders. Each finding
# with a known tool id gets a tertiary button labelled
# ``"{Tool display name} →"`` that switches pages on click. Findings
# with no tool id (file-level CSV-shape warnings, encoding flags, etc.)
# render without a button — the description still shows so the user
# isn't blind to them.

class TestRowsRenderForFindings:
    def test_one_button_per_targeted_finding(self):
        findings = [
            _make_finding(tool="02_text_cleaner", id="whitespace_padding"),
            _make_finding(tool="02_text_cleaner", id="nbsp_padding"),
            _make_finding(tool="03_format_standardizer", id="mixed_case_email"),
        ]
        app = _harness(findings)
        app.run()
        labels = [b.label for b in app.button]
        # Each targeted finding gets its own "Open Tool" button — three
        # findings → three buttons (two pointing at Clean Text, one at
        # Standardize Formats).
        clean_text_buttons = [l for l in labels if l == "Clean Text →"]
        format_buttons = [l for l in labels if l == "Standardize Formats →"]
        assert len(clean_text_buttons) == 2, (
            f"expected 2 Clean Text buttons; got: {labels}"
        )
        assert len(format_buttons) == 1, (
            f"expected 1 Standardize Formats button; got: {labels}"
        )

    def test_tool_names_localize_in_spanish(self):
        findings = [_make_finding(tool="02_text_cleaner")]
        app = _harness(findings, lang="es")
        app.run()
        labels = [b.label for b in app.button]
        assert any("Limpiar texto" in lbl for lbl in labels), (
            f"Spanish tool name missing; buttons: {labels}"
        )


# ---------------------------------------------------------------------------
# Open-tool button labels — confirm the arrow + name format
# ---------------------------------------------------------------------------

class TestOpenToolButton:
    """Each finding with a known tool gets a tertiary button labelled
    ``"{Tool name} →"``. The arrow + spacing is the affordance that
    distinguishes the row's primary action from the title text."""

    def test_open_tool_label_english(self):
        findings = [_make_finding(tool="02_text_cleaner")]
        app = _harness(findings)
        app.run()
        labels = [b.label for b in app.button]
        assert "Clean Text →" in labels, (
            f"expected 'Clean Text →' button; got: {labels}"
        )

    def test_open_tool_label_spanish(self):
        findings = [_make_finding(tool="02_text_cleaner")]
        app = _harness(findings, lang="es")
        app.run()
        labels = [b.label for b in app.button]
        assert "Limpiar texto →" in labels, (
            f"expected 'Limpiar texto →' button; got: {labels}"
        )


# ---------------------------------------------------------------------------
# Untargeted findings (file-level) render without an action button
# ---------------------------------------------------------------------------

class TestUntargetedFindings:
    """A finding with ``tool=""`` (e.g., CSV BOM stripped at read time)
    is file-level — no tool page to jump to — and the redesigned panel
    renders the description without a button. We assert that the row
    contributes nothing to ``app.button`` while still appearing in the
    rendered markdown."""

    def test_untargeted_renders_no_button_en(self):
        findings = [
            _make_finding(tool="", id="csv_bom_stripped", description="BOM stripped"),
            _make_finding(tool="02_text_cleaner", id="nbsp_padding"),
        ]
        app = _harness(findings)
        app.run()
        labels = [b.label for b in app.button]
        # Only the targeted finding contributed a button.
        assert "Clean Text →" in labels
        # The BOM finding's description must still be visible somewhere.
        all_md = "\n".join(
            m.body for m in app.markdown if hasattr(m, "body")
        )
        assert "BOM stripped" in all_md, (
            "untargeted finding's description should still render"
        )

    def test_untargeted_renders_no_button_es(self):
        findings = [_make_finding(
            tool="", id="csv_bom_stripped", description="BOM eliminado",
        )]
        app = _harness(findings, lang="es")
        app.run()
        labels = [b.label for b in app.button]
        # No tool id → no tool-jump button at all.
        assert not any("→" in lbl for lbl in labels), (
            f"untargeted finding should not render a tool button; got: {labels}"
        )


# ---------------------------------------------------------------------------
# Severity summary
# ---------------------------------------------------------------------------

class TestSeveritySummary:
    """The panel renders a per-severity summary caption like
    ``⚠️ 2 warn · ℹ️ 1 info``. We pin the icon + count rendering."""

    def test_severity_icons_render(self):
        findings = [
            _make_finding(tool="02_text_cleaner", severity="warn"),
            _make_finding(tool="02_text_cleaner", severity="warn"),
            _make_finding(tool="03_format_standardizer", severity="info"),
        ]
        app = _harness(findings)
        app.run()
        text = collected_text(app)
        # Icons live in the per-language pack ("findings.severity_*").
        # The summary template is shared between languages.
        assert "⚠️" in text or "warn" in text
        # Counts present.
        assert "2 warn" in text or "2  warn" in text
