"""
Accessibility Backend Tests
Tests for HTML structure and semantic markup accessibility
"""

import re
import pytest
from bs4 import BeautifulSoup


class TestHTMLAccessibility:
    """Test HTML structure for accessibility compliance"""

    @pytest.fixture
    def research_page_html(self, authenticated_client):
        """Fetch the research page HTML"""
        response = authenticated_client.get("/")
        assert response.status_code == 200
        return BeautifulSoup(response.data, "html.parser")

    def test_form_has_proper_labels(self, research_page_html):
        """Test that all form inputs have proper labels"""
        soup = research_page_html

        # Find all input elements that should have labels
        inputs = soup.find_all(["input", "textarea", "select"])

        # Track which inputs need labels
        inputs_needing_labels = []

        for input_element in inputs:
            input_id = input_element.get("id")
            input_type = input_element.get("type", "text")
            input_name = input_element.get("name", "")

            # Skip hidden inputs, buttons, and CSRF tokens
            if (
                input_type in ["hidden", "submit", "button"]
                or input_name == "csrf_token"
            ):
                continue

            # Check for associated label
            if input_id:
                label = soup.find("label", {"for": input_id})
                if label is None:
                    inputs_needing_labels.append(
                        f"Input with id '{input_id}' (type: {input_type})"
                    )

        # Allow some flexibility - not all inputs may need labels in modern UI
        if len(inputs_needing_labels) > 0:
            print(
                f"Note: {len(inputs_needing_labels)} inputs without labels found: {inputs_needing_labels}"
            )

    def test_radio_button_structure(self, research_page_html):
        """Test that radio buttons are properly structured"""
        soup = research_page_html

        # Find radio button group - might be named differently
        radio_inputs = soup.find_all("input", {"type": "radio"})

        if len(radio_inputs) == 0:
            pytest.skip(
                "No radio buttons found on page - might be using different UI"
            )

        # Check that radio buttons have proper structure
        for radio in radio_inputs:
            assert radio.get("name") is not None, (
                "Radio button should have a name attribute"
            )
            radio_id = radio.get("id")
            if radio_id:
                # Check for associated label
                label = soup.find("label", {"for": radio_id})
                assert label is not None, (
                    f"Radio button with id '{radio_id}' should have a label"
                )

    def test_fieldset_and_legend(self, research_page_html):
        """Test that form groups have proper fieldset and legend"""
        soup = research_page_html

        # Check if there are any fieldsets
        fieldsets = soup.find_all("fieldset")

        # Modern forms might not use fieldsets
        if len(fieldsets) == 0:
            # Check for alternative grouping methods
            form_groups = soup.find_all(
                class_=re.compile(r"form-group|field-group|input-group")
            )
            assert len(form_groups) > 0 or len(soup.find_all("form")) > 0, (
                "Should have some form of input grouping (fieldset, form-group, or form)"
            )
        else:
            # If fieldsets exist, they should have legends
            for fieldset in fieldsets:
                legend = fieldset.find("legend")
                if legend is None:
                    # Check for aria-label as alternative
                    aria_label = fieldset.get("aria-label") or fieldset.get(
                        "aria-labelledby"
                    )
                    assert aria_label is not None, (
                        "Fieldset should have a legend or aria-label"
                    )

    def test_aria_attributes(self, research_page_html):
        """Test that ARIA attributes are properly set where needed"""
        soup = research_page_html

        # Check for any interactive elements
        interactive_elements = soup.find_all(
            ["button", "a", "input", "select", "textarea"]
        )

        # Check that interactive elements have appropriate accessibility attributes
        for element in interactive_elements:
            element_type = element.name
            element_text = element.get_text(strip=True)

            # Buttons and links should have accessible text
            if element_type in ["button", "a"]:
                aria_label = element.get("aria-label")
                title = element.get("title")

                # Should have text content, aria-label, or title
                assert element_text or aria_label or title, (
                    f"{element_type} element should have accessible text"
                )

    def test_keyboard_hints_present(self, research_page_html):
        """Test that keyboard navigation hints are present"""
        soup = research_page_html

        # Look for keyboard hint indicators - not all apps show these
        # Modern apps might show keyboard hints differently
        # Check for focusable elements at minimum
        focusable_elements = soup.find_all(
            ["a", "button", "input", "select", "textarea"]
        )
        assert len(focusable_elements) > 0, (
            "Page should have focusable elements"
        )

    def test_form_structure(self, research_page_html):
        """Test that forms have proper structure"""
        soup = research_page_html

        forms = soup.find_all("form")

        if len(forms) == 0:
            # Might be a single-page app with AJAX
            # Check for input elements at least
            inputs = soup.find_all(["input", "textarea", "select"])
            assert len(inputs) > 0, "Page should have some form inputs"
        else:
            for form in forms:
                # Check form has action or is handled by JavaScript
                action = form.get("action")
                form_id = form.get("id")
                form_class = form.get("class", [])

                # Form should have some identifier or action
                assert action or form_id or form_class, (
                    "Form should have action, id, or class"
                )

    def test_semantic_markup(self, research_page_html):
        """Test that semantic HTML5 elements are used"""
        soup = research_page_html

        # Check for semantic elements
        semantic_elements = [
            "header",
            "nav",
            "main",
            "footer",
            "section",
            "article",
        ]
        found_semantic = []

        for element in semantic_elements:
            if soup.find(element):
                found_semantic.append(element)

        # Should use at least some semantic elements
        assert len(found_semantic) > 0, (
            f"Should use semantic HTML5 elements. Found: {found_semantic}"
        )

    def test_required_fields_marked(self, research_page_html):
        """Test that required fields are properly marked"""
        soup = research_page_html

        # Find all required inputs
        required_inputs = soup.find_all(attrs={"required": True})

        for input_elem in required_inputs:
            input_id = input_elem.get("id")

            # Check for visual indicator
            if input_id:
                label = soup.find("label", {"for": input_id})
                if label:
                    label_text = label.get_text()
                    # Should have some indicator like *, (required), etc.
                    has_indicator = (
                        "*" in label_text
                        or "required" in label_text.lower()
                        or label.find(class_=re.compile(r"required"))
                    )

                    # Also check for aria-required
                    aria_required = input_elem.get("aria-required") == "true"

                    assert has_indicator or aria_required, (
                        f"Required field '{input_id}' should have visual indicator or aria-required"
                    )

    def test_error_handling_structure(self, research_page_html):
        """Test that error messages have proper structure"""
        soup = research_page_html

        # Look for error message containers
        error_containers = soup.find_all(
            class_=re.compile(r"error|alert|message|flash")
        )

        # Page should have some way to display errors
        assert len(error_containers) > 0 or soup.find(
            attrs={"role": "alert"}
        ), (
            "Should have containers for error messages (class containing 'error', 'alert', or role='alert')"
        )


class TestAccessibilityConfiguration:
    """Test accessibility-related configuration and styling"""

    def test_css_focus_styles(self, authenticated_client):
        """Test that CSS includes focus styles"""
        # Try to get main CSS file
        response = authenticated_client.get("/static/css/style.css")

        if response.status_code == 200:
            css_content = response.data.decode("utf-8")

            # Check for focus styles
            has_focus_styles = (
                ":focus" in css_content
                or "focus-visible" in css_content
                or "outline" in css_content
            )

            assert has_focus_styles, (
                "CSS should include focus styles for keyboard navigation"
            )
        else:
            # CSS might be bundled or in different location
            pytest.skip("Could not access main CSS file")

    def test_responsive_meta_tag(self, authenticated_client):
        """Test that pages have responsive viewport meta tag"""
        response = authenticated_client.get("/")
        assert response.status_code == 200

        soup = BeautifulSoup(response.data, "html.parser")
        viewport_meta = soup.find("meta", {"name": "viewport"})

        assert viewport_meta is not None, "Should have viewport meta tag"
        content = viewport_meta.get("content", "")
        assert "width=device-width" in content, "Viewport should be responsive"

    def test_lang_attribute(self, authenticated_client):
        """Test that HTML has lang attribute"""
        response = authenticated_client.get("/")
        assert response.status_code == 200

        soup = BeautifulSoup(response.data, "html.parser")
        html_tag = soup.find("html")

        assert html_tag is not None, "Should have html tag"
        lang = html_tag.get("lang")
        assert lang is not None, "HTML tag should have lang attribute"
        assert lang in ["en", "en-US", "en-GB"], (
            f"Lang should be English variant, got: {lang}"
        )

    def test_skip_navigation_link(self, authenticated_client):
        """Test for skip navigation link"""
        response = authenticated_client.get("/")
        assert response.status_code == 200

        soup = BeautifulSoup(response.data, "html.parser")

        # Look for skip link
        skip_link = soup.find("a", text=re.compile(r"skip|main content", re.I))

        # Skip link is a best practice but not always present
        if skip_link:
            href = skip_link.get("href")
            assert href and href.startswith("#"), (
                "Skip link should be an anchor link"
            )

    def test_heading_hierarchy(self, authenticated_client):
        """Test that headings follow proper hierarchy"""
        response = authenticated_client.get("/")
        assert response.status_code == 200

        soup = BeautifulSoup(response.data, "html.parser")

        # Find all headings
        headings = soup.find_all(["h1", "h2", "h3", "h4", "h5", "h6"])

        if len(headings) > 0:
            # Should have at least one h1
            h1_tags = soup.find_all("h1")
            assert len(h1_tags) >= 1, "Page should have at least one h1 tag"

            # Check heading order doesn't skip levels
            heading_levels = [int(h.name[1]) for h in headings]
            for i in range(1, len(heading_levels)):
                level_diff = heading_levels[i] - heading_levels[i - 1]
                assert level_diff <= 1, (
                    f"Heading hierarchy should not skip levels. "
                    f"Found h{heading_levels[i - 1]} followed by h{heading_levels[i]}"
                )
