"""Framework identity regression contract: html stays html, react_app stays
react_app, nuxt_app stays nuxt_app through runtime-substrate assembly and Starter
overlay assembly.

``project_type``/``framework`` is a plain ``str`` throughout
``src/domain/starter_library`` (not a typed enum) -- these tests pin the invariant
that needs preserving, without changing that representation.
"""
from __future__ import annotations

import pytest
from src.domain.starter_library.archetypes import known_archetype_ids
from src.domain.starter_library.assembler import assemble_starter_files
from src.domain.starter_library.runtime_substrate import (
    PAGES_SUBSTRATE_TYPES,
    assemble_generated_project,
    assemble_runtime_substrate,
)

FRAMEWORKS = ("html", "react_app", "nuxt_app")


def test_pages_substrate_types_are_exactly_the_three_frameworks():
    assert PAGES_SUBSTRATE_TYPES == frozenset(FRAMEWORKS)


@pytest.mark.parametrize("project_type", FRAMEWORKS)
def test_runtime_substrate_never_mixes_framework_markers(project_type):
    """Each framework's substrate contains only its own distinctive files, never
    another framework's -- Nuxt's .vue/nuxt.config, React's package.json + .tsx, or
    a bare HTML shell, must not leak across project types."""
    files = assemble_runtime_substrate(project_type)

    has_nuxt_markers = "nuxt.config.ts" in files or any(f.endswith(".vue") for f in files)
    has_react_markers = any(f.endswith((".tsx", ".jsx")) for f in files)

    if project_type == "nuxt_app":
        assert has_nuxt_markers and not has_react_markers
    elif project_type == "react_app":
        assert has_react_markers and not has_nuxt_markers
    else:
        assert project_type == "html"
        assert not has_nuxt_markers and not has_react_markers
        assert "package.json" not in files


def test_an_unsupported_project_type_fails_closed_in_runtime_substrate():
    with pytest.raises(ValueError):
        assemble_runtime_substrate("wordpress_theme")


@pytest.mark.parametrize("framework", FRAMEWORKS)
def test_starter_overlay_file_extensions_match_the_requested_framework(framework):
    """Across every known archetype starter, the overlay's own page/component files
    (not shared JSON) use that framework's file extension -- never another one's."""
    page_extension = {"html": None, "react_app": ".jsx", "nuxt_app": ".vue"}[framework]
    other_extensions = {".jsx", ".vue"} - ({page_extension} if page_extension else set())

    for starter_id in sorted(known_archetype_ids()):
        files = assemble_starter_files(starter_id, framework=framework)
        for relative in files:
            for other in other_extensions:
                assert not relative.endswith(other), (
                    f"{starter_id}/{framework} overlay produced {relative!r}, "
                    f"which belongs to a different framework"
                )


def test_assemble_generated_project_overlay_matches_the_requested_framework():
    starter_id = sorted(known_archetype_ids())[0]
    react_files = assemble_generated_project("react_app", starter_id=starter_id)
    nuxt_files = assemble_generated_project("nuxt_app", starter_id=starter_id)

    assert any(f.endswith(".jsx") for f in react_files)
    assert not any(f.endswith(".vue") for f in react_files)
    assert any(f.endswith(".vue") for f in nuxt_files)
    assert not any(f.endswith(".jsx") for f in nuxt_files)


def test_unrecognized_framework_fails_closed_instead_of_leaking_into_nuxt():
    """assemble_starter_files used to silently default an unrecognized framework
    string to nuxt_app's overlay (a wrong-framework leak); it now raises, matching
    assemble_runtime_substrate's existing behavior for an unsupported project_type."""
    starter_id = sorted(known_archetype_ids())[0]

    with pytest.raises(ValueError):
        assemble_starter_files(starter_id, framework="shopify_theme")
