"""Assemble a thin archetype shell as publishable application source.

The assembler emits layout/nav/route files only. It does not create private
backends, Cloud services, or unfrozen entities. Connected-repo callers must
not use this to overwrite an existing application tree.
"""

from __future__ import annotations

import json

from src.domain.starter_library.archetypes import ArchetypeStarter, get_archetype_starter

_PRIVATE_MARKERS = (
    "association_engine",
    "board_engine",
    "field_service_engine",
    "audit_engine",
    "b2b_order_engine",
    "customer_success_engine",
    "cloudgate.p2hs.com",
    "VITE_P2HS_GATEWAY_URL",
    "NUXT_PUBLIC_P2HS_GATEWAY_URL",
    "VITE_P2HS_CLOUD_PUBLIC_URL",
    "NUXT_PUBLIC_P2HS_CLOUD_PUBLIC_URL",
    "VITE_P2HS_CORE_PUBLIC_URL",
    "NUXT_PUBLIC_P2HS_CORE_PUBLIC_URL",
)


_KNOWN_FRAMEWORKS = frozenset({"html", "react_app", "nuxt_app"})


def assemble_starter_files(
    starter_id: str | None,
    *,
    framework: str = "nuxt_app",
) -> dict[str, str]:
    """Framework identity must survive this call: an unrecognized ``framework``
    used to silently fall through to Nuxt's overlay (a wrong-framework leak, not a
    safe default) -- it now fails closed instead, matching the sibling
    ``assemble_runtime_substrate``'s existing ``raise ValueError`` for an
    unsupported ``project_type``. See
    tests/test_starter_library_framework_identity.py."""
    if framework not in _KNOWN_FRAMEWORKS:
        raise ValueError(f"no starter overlay for framework {framework!r}")
    starter = get_archetype_starter(starter_id)
    if starter is None:
        return {}
    if framework == "react_app":
        return _react_files(starter)
    if framework == "html":
        return _html_files(starter)
    return _nuxt_files(starter)


def assert_thin_shell(files: dict[str, str]) -> None:
    joined = "\n".join(files.values())
    for marker in _PRIVATE_MARKERS:
        if marker in joined:
            raise ValueError(f"starter shell leaked {marker}")
    for relative in files:
        if relative.startswith(("server/api/", "functions/api/", "cloud/")):
            raise ValueError(f"starter shell must not add backend {relative}")


def _nav_json(starter: ArchetypeStarter) -> str:
    payload = {
        "id": starter.id,
        "version": starter.version,
        "primary_route": starter.primary_route,
        "workflow_emphasis": starter.workflow_emphasis,
        "surfaces": starter.supported_surfaces,
        "navigation": [item.model_dump() for item in starter.default_navigation],
        "routes": [item.model_dump() for item in starter.default_routes],
        "bff": "/api",
    }
    return json.dumps(payload, indent=2) + "\n"


def _layout_vue(starter: ArchetypeStarter) -> str:
    links = "\n".join(
        f'        <NuxtLink v-if="surface === \'{item.surface}\'" to="{item.href}">{item.label}</NuxtLink>'
        for item in starter.default_navigation
    )
    return f"""<template>
  <div class="starter-shell" data-starter="{starter.id}" data-emphasis="{starter.workflow_emphasis}">
    <header>
      <nav aria-label="Primary">
{links}
      </nav>
    </header>
    <main>
      <slot />
    </main>
  </div>
</template>

<script setup>
const surface = '{starter.supported_surfaces[0] if starter.supported_surfaces else "staff"}'
</script>
"""


def _page_vue(route_title: str, block_id: str | None, path: str) -> str:
    block = block_id or "layout.admin"
    return f"""<template>
  <section data-app-block="{block}" data-route="{path}">
    <h1>{route_title}</h1>
    <p class="starter-placeholder">Mounted from the {block} App Block host. Data goes through /api.</p>
  </section>
</template>
"""


def _nuxt_route_file(path: str) -> str:
    relative = path.strip("/").replace(":", "")
    if not relative:
        return "pages/index.vue"
    if relative.startswith("admin"):
        return f"pages/{relative}/index.vue" if ":id" not in path else f"pages/{relative}.vue"
    return f"pages/{relative}/index.vue"


def _nuxt_files(starter: ArchetypeStarter) -> dict[str, str]:
    files = {
        "layouts/starter.vue": _layout_vue(starter),
        "public/starter-shell.json": _nav_json(starter),
    }
    for route in starter.default_routes:
        if ":id" in route.path:
            files["pages/admin/cases/[id].vue" if "cases" in route.path else _nuxt_route_file(route.path)] = (
                _page_vue(route.title, route.primary_block, route.path)
            )
            continue
        files[_nuxt_route_file(route.path)] = _page_vue(
            route.title, route.primary_block, route.path
        )
    assert_thin_shell(files)
    return files


def _react_files(starter: ArchetypeStarter) -> dict[str, str]:
    nav = "\n".join(
        f'          <Link to="{item.href}">{item.label}</Link>'
        for item in starter.default_navigation
    )
    files = {
        "src/layouts/StarterShell.jsx": f"""import {{ Link, Outlet }} from "react-router-dom";

export default function StarterShell() {{
  return (
    <div className="starter-shell" data-starter="{starter.id}">
      <nav aria-label="Primary">
{nav}
      </nav>
      <main>
        <Outlet />
      </main>
    </div>
  );
}}
""",
        "public/starter-shell.json": _nav_json(starter),
    }
    assert_thin_shell(files)
    return files


def _html_files(starter: ArchetypeStarter) -> dict[str, str]:
    files = {"starter-shell.json": _nav_json(starter)}
    assert_thin_shell(files)
    return files
