"""
Test JavaScript URL configuration consistency
This tests that the JavaScript URL configuration matches the backend routes
"""

import re
from pathlib import Path

import pytest

from src.local_deep_research.web.routes.route_registry import (
    get_all_routes,
)


class TestJavaScriptURLConfiguration:
    """Test that JavaScript URL configuration matches backend routes"""

    @pytest.fixture
    def js_urls_content(self):
        """Load the JavaScript URLs configuration file"""
        js_file_path = (
            Path(__file__).parent.parent.parent
            / "src"
            / "local_deep_research"
            / "web"
            / "static"
            / "js"
            / "config"
            / "urls.js"
        )
        with open(js_file_path, "r") as f:
            return f.read()

    def extract_js_urls(self, js_content):
        """Extract URL patterns from JavaScript content"""
        urls = {}

        # More robust extraction pattern that handles multiline and comments
        # Extract the URLS object
        urls_match = re.search(
            r"const\s+URLS\s*=\s*\{(.*?)\n\};", js_content, re.DOTALL
        )
        if not urls_match:
            return urls

        urls_content = urls_match.group(1)

        # Extract each section
        sections = {
            "API": r"API:\s*\{(.*?)\n\s*\}",
            "PAGES": r"PAGES:\s*\{(.*?)\n\s*\}",
            "HISTORY_API": r"HISTORY_API:\s*\{(.*?)\n\s*\}",
            "SETTINGS_API": r"SETTINGS_API:\s*\{(.*?)\n\s*\}",
            "METRICS_API": r"METRICS_API:\s*\{(.*?)\n\s*\}",
        }

        for section_name, section_pattern in sections.items():
            section_match = re.search(section_pattern, urls_content, re.DOTALL)
            if section_match:
                section_content = section_match.group(1)
                # Extract key-value pairs, handling comments and multiline
                kv_pattern = r"(\w+):\s*'([^']+)'(?:\s*,|\s*(?://[^\n]*)?\s*$)"
                for match in re.finditer(kv_pattern, section_content):
                    key, url = match.groups()
                    urls[f"{section_name}.{key}"] = url

        return urls

    def normalize_url_pattern(self, url):
        """Normalize URL patterns for comparison"""
        # Replace {id} with Flask-style <int:id> or <id>
        url = re.sub(r"\{id\}", "<string:research_id>", url)
        url = re.sub(r"\{key\}", "<path:key>", url)
        url = re.sub(r"\{(\w+)\}", r"<\1>", url)
        return url

    def test_js_urls_exist(self, js_urls_content):
        """Test that the JavaScript URLs file exists and has content"""
        assert js_urls_content
        assert "URLS" in js_urls_content
        assert "URLBuilder" in js_urls_content

    def test_critical_api_urls_match_backend(self, js_urls_content):
        """Test that critical API URLs in JS match backend routes"""
        js_urls = self.extract_js_urls(js_urls_content)
        backend_routes = get_all_routes()

        # Create a map of backend routes for easy lookup
        backend_map = {}
        for route in backend_routes:
            # Normalize the path for comparison
            path = route["path"]
            backend_map[path] = route

        # Critical URLs that must match
        critical_mappings = [
            ("API.START_RESEARCH", "/api/start_research"),
            ("API.RESEARCH_STATUS", "/api/research/<research_id>/status"),
            ("API.RESEARCH_DETAILS", "/api/research/<string:research_id>"),
            ("API.RESEARCH_LOGS", "/api/research/<string:research_id>/logs"),
            ("API.RESEARCH_REPORT", "/api/report/<string:research_id>"),
            ("API.TERMINATE_RESEARCH", "/api/terminate/<string:research_id>"),
            ("API.DELETE_RESEARCH", "/api/delete/<string:research_id>"),
            ("API.HISTORY", "/history/api"),
            ("PAGES.HOME", "/"),
            ("PAGES.HISTORY", "/history/"),
            ("PAGES.SETTINGS", "/settings/"),
            ("PAGES.METRICS", "/metrics/"),
        ]

        for js_key, expected_backend_path in critical_mappings:
            assert js_key in js_urls, f"Missing JS URL key: {js_key}"

            js_url = js_urls[js_key]
            normalized_js_url = self.normalize_url_pattern(js_url)

            # Check if the URL exists in backend (with some flexibility for parameter names)
            found = False
            for backend_path in backend_map:
                if self.urls_match(normalized_js_url, backend_path):
                    found = True
                    break

            assert found, (
                f"JS URL {js_key}='{js_url}' not found in backend routes"
            )

    def urls_match(self, js_url, backend_url):
        """Check if two URLs match, accounting for parameter differences"""
        # Normalize both URLs
        js_parts = js_url.strip("/").split("/")
        backend_parts = backend_url.strip("/").split("/")

        if len(js_parts) != len(backend_parts):
            return False

        for js_part, backend_part in zip(js_parts, backend_parts, strict=False):
            # If either part is a parameter, consider it a match
            if "<" in js_part or "<" in backend_part:
                continue
            # Otherwise, parts must match exactly
            if js_part != backend_part:
                return False

        return True

    def test_url_builder_methods_exist(self, js_urls_content):
        """Test that URLBuilder utility methods are defined"""
        expected_methods = [
            "build",
            "buildWithReplacements",
            "progressPage",
            "resultsPage",
            "detailsPage",
            "researchStatus",
            "researchDetails",
            "researchLogs",
            "researchReport",
            "terminateResearch",
            "deleteResearch",
            "extractResearchId",
            "getCurrentPageType",
        ]

        for method in expected_methods:
            assert (
                f"{method}(" in js_urls_content
                or f"{method}:" in js_urls_content
            ), f"URLBuilder method '{method}' not found"

    def test_no_hardcoded_urls_in_pattern(self, js_urls_content):
        """Test that URLs follow consistent patterns"""
        js_urls = self.extract_js_urls(js_urls_content)

        # Check for consistent parameter naming
        for key, url in js_urls.items():
            # Research ID parameters should use {id}
            if "research" in key.lower() and "{" in url:
                assert "{id}" in url or "{research_id}" in url, (
                    f"Research URL {key} should use {{id}} parameter: {url}"
                )

            # No URLs should have trailing slashes except root paths
            if (
                url != "/"
                and not url.endswith("/{id}")
                and not url.endswith("/{key}")
            ):
                # Allow trailing slash for directory-like paths
                if url.count("/") > 2:
                    assert not url.endswith("//"), (
                        f"URL {key} has double slash: {url}"
                    )

    def test_api_url_patterns(self, js_urls_content):
        """Test that API URLs follow RESTful patterns"""
        js_urls = self.extract_js_urls(js_urls_content)
        api_urls = {k: v for k, v in js_urls.items() if k.startswith("API.")}

        for key, url in api_urls.items():
            # API URLs should start with /api (with some exceptions)
            if key not in ["API.HISTORY", "API.HEALTH"]:  # Known exceptions
                assert url.startswith("/api"), (
                    f"API URL {key} should start with /api: {url}"
                )

            # Check REST patterns
            if "DELETE" in key:
                # Delete operations should have an ID parameter
                assert "{id}" in url, (
                    f"DELETE URL {key} should have ID parameter: {url}"
                )

            if "SAVE" in key or "CREATE" in key:
                # Save/Create operations typically don't have ID in URL
                if (
                    "RATING" not in key
                ):  # Exception for ratings which need research ID
                    assert "{id}" not in url, (
                        f"CREATE/SAVE URL {key} shouldn't have ID in path: {url}"
                    )

    def test_settings_api_consistency(self, js_urls_content):
        """Test that settings API URLs are consistent"""
        js_urls = self.extract_js_urls(js_urls_content)
        settings_urls = {
            k: v for k, v in js_urls.items() if k.startswith("SETTINGS_API.")
        }

        # All settings API URLs should start with /settings/
        for key, url in settings_urls.items():
            assert url.startswith("/settings/"), (
                f"Settings API URL {key} should start with /settings/: {url}"
            )

        # Check that CRUD operations exist
        assert "SETTINGS_API.GET_SETTING" in js_urls
        assert "SETTINGS_API.UPDATE_SETTING" in js_urls
        assert "SETTINGS_API.DELETE_SETTING" in js_urls

    def test_metrics_api_consistency(self, js_urls_content):
        """Test that metrics API URLs are consistent"""
        js_urls = self.extract_js_urls(js_urls_content)
        metrics_urls = {
            k: v for k, v in js_urls.items() if k.startswith("METRICS_API.")
        }

        # All metrics API URLs should start with /metrics/
        for key, url in metrics_urls.items():
            assert url.startswith("/metrics/"), (
                f"Metrics API URL {key} should start with /metrics/: {url}"
            )

        # Check research-specific metrics URLs have ID parameter
        research_metrics = [
            "RESEARCH",
            "RESEARCH_TIMELINE",
            "RESEARCH_SEARCH",
            "RATINGS_GET",
            "RATINGS_SAVE",
            "RESEARCH_COSTS",
        ]
        for metric in research_metrics:
            key = f"METRICS_API.{metric}"
            if key in js_urls:
                assert "{id}" in js_urls[key], (
                    f"Metrics URL {key} should have {{id}} parameter: {js_urls[key]}"
                )

    def test_page_routes_consistency(self, js_urls_content):
        """Test that page routes are consistent"""
        js_urls = self.extract_js_urls(js_urls_content)

        # Pages with IDs should have {id} parameter
        id_pages = ["PROGRESS", "RESULTS", "DETAILS"]
        for page in id_pages:
            key = f"PAGES.{page}"
            if key in js_urls:
                assert "{id}" in js_urls[key], (
                    f"Page URL {key} should have {{id}} parameter: {js_urls[key]}"
                )

        # Root pages should end with /
        root_pages = ["HOME", "HISTORY", "SETTINGS", "METRICS"]
        for page in root_pages:
            key = f"PAGES.{page}"
            if key in js_urls and page != "HOME":  # HOME is just '/'
                assert js_urls[key].endswith("/"), (
                    f"Root page URL {key} should end with /: {js_urls[key]}"
                )

    def test_url_builder_convenience_methods_match_urls(self, js_urls_content):
        """Test that URLBuilder convenience methods use correct URL constants"""
        # Extract method implementations
        method_checks = [
            ("progressPage", "URLS.PAGES.PROGRESS"),
            ("resultsPage", "URLS.PAGES.RESULTS"),
            ("detailsPage", "URLS.PAGES.DETAILS"),
            ("researchStatus", "URLS.API.RESEARCH_STATUS"),
            ("researchDetails", "URLS.API.RESEARCH_DETAILS"),
            ("researchLogs", "URLS.API.RESEARCH_LOGS"),
            ("researchReport", "URLS.API.RESEARCH_REPORT"),
            ("terminateResearch", "URLS.API.TERMINATE_RESEARCH"),
            ("deleteResearch", "URLS.API.DELETE_RESEARCH"),
        ]

        for method_name, expected_url_const in method_checks:
            # Find the method implementation
            method_pattern = rf"{method_name}\([^)]*\)\s*\{{[^}}]*\}}"
            method_match = re.search(method_pattern, js_urls_content, re.DOTALL)

            assert method_match, f"Method {method_name} not found"

            method_body = method_match.group(0)
            assert expected_url_const in method_body, (
                f"Method {method_name} should use {expected_url_const}"
            )

    def test_no_duplicate_urls(self, js_urls_content):
        """Test that there are no duplicate URL patterns"""
        js_urls = self.extract_js_urls(js_urls_content)

        # Normalize URLs for comparison
        normalized_urls = {}
        for key, url in js_urls.items():
            normalized = self.normalize_url_pattern(url)
            if normalized in normalized_urls:
                # Allow some known duplicates
                allowed_duplicates = [
                    # RESTful APIs use same URL with different methods
                    ("SETTINGS_API.GET_SETTING", "SETTINGS_API.UPDATE_SETTING"),
                    (
                        "SETTINGS_API.UPDATE_SETTING",
                        "SETTINGS_API.DELETE_SETTING",
                    ),
                    ("SETTINGS_API.GET_SETTING", "SETTINGS_API.DELETE_SETTING"),
                    ("METRICS_API.RATINGS_GET", "METRICS_API.RATINGS_SAVE"),
                    # History API might share patterns with main API
                    ("HISTORY_API", "API"),
                ]

                # Check if this is an allowed duplicate
                is_allowed = False
                for pattern1, pattern2 in allowed_duplicates:
                    if (
                        key.startswith(pattern1)
                        and normalized_urls[normalized].startswith(pattern2)
                    ) or (
                        key.startswith(pattern2)
                        and normalized_urls[normalized].startswith(pattern1)
                    ):
                        is_allowed = True
                        break

                if not is_allowed:
                    pytest.fail(
                        f"Duplicate URL pattern found:\n"
                        f"  {key} = {url}\n"
                        f"  {normalized_urls[normalized]} = {url}\n"
                        f"  (normalized: {normalized})"
                    )
            normalized_urls[normalized] = key
