"""Dispatch-time planning for the static HTML -> Nuxt conversion (static_conversion.py).

Only a deliberate migrate/replace of a Nuxt project with imported source is converted; the plan lists the files to
write, deletes the stale static copies that would shadow the pages, keeps protected routes out, and tells the agent
what was done and what is left for it."""

from __future__ import annotations

from types import SimpleNamespace

import pytest
from src.domain.codegen.static_conversion import (
    PROTECTED_NAMES,
    build_appendix,
    is_eligible,
    plan_conversion,
    protected_routes,
)

SITE = {
    "index.html": "<title>Home</title><h1>Home</h1><script src=assets/js/app.js></script>",
    "about.html": "<title>About</title><p>about</p>",
    "admin.html": "<title>Admin</title><p>a prototype admin mock</p>",
    "assets/js/app.js": "var a=1",
    "assets/css/site.css": "a{}",
}
CTX = {"source_adoption": "migrate", "import_snapshot": {"import_id": "x", "files": [{"path": "index.html"}]}}


def settings(enabled=True):
    return SimpleNamespace(codegen_static_conversion_enabled=enabled)


# ---- who is eligible ---------------------------------------------------------------------------


def test_only_a_deliberate_migrate_or_replace_of_a_nuxt_project_with_imported_source_qualifies():
    assert is_eligible(settings(), "nuxt_app", CTX)
    assert is_eligible(settings(), "nuxt_app", {**CTX, "source_adoption": "replace"})


@pytest.mark.parametrize("adoption", [None, "add", "merge", "inject"])
def test_the_other_adoption_modes_never_convert(adoption):
    assert not is_eligible(settings(), "nuxt_app", {**CTX, "source_adoption": adoption})


@pytest.mark.parametrize("project_type", ["html", "react_app", "wordpress_companion", None, ""])
def test_only_a_nuxt_project_is_converted(project_type):
    assert not is_eligible(settings(), project_type, CTX)


def test_it_is_off_unless_the_flag_is_on():
    assert not is_eligible(settings(False), "nuxt_app", CTX)
    assert not is_eligible(SimpleNamespace(), "nuxt_app", CTX)  # a config without the flag


@pytest.mark.parametrize("snapshot", [None, {}, {"files": []}, "x"])
def test_without_imported_source_there_is_nothing_to_convert(snapshot):
    assert not is_eligible(settings(), "nuxt_app", {**CTX, "import_snapshot": snapshot})
    assert not is_eligible(settings(), "nuxt_app", None)


# ---- the plan ----------------------------------------------------------------------------------------


def test_the_plan_writes_pages_markup_composable_and_text_assets():
    plan = plan_conversion(SITE, existing_paths=set())
    written = {p for p, c in plan.files.items() if c is not None}
    assert {"pages/index.vue", "pages/about.html.vue", "pages/about.vue", "legacy/pages/index.html",
            "legacy/pages/about.html", "composables/useLegacyScripts.ts", "public/assets/js/app.js",
            "public/assets/css/site.css"} <= written
    assert plan.summary["files"] == len(written) and plan.deleted == []


def test_a_prototype_page_named_like_a_protected_route_is_reported_and_never_converted():
    plan = plan_conversion(SITE, existing_paths=set())
    assert "pages/admin.html.vue" not in plan.files and "pages/admin.vue" not in plan.files
    assert [s["path"] for s in plan.summary["skipped"]] == ["admin.html"]
    assert "`admin.html`" in plan.appendix and "Not converted" in plan.appendix


def test_the_protected_names_cover_auth_and_admin_and_both_url_forms():
    assert {"admin", "login", "signup", "api", "account"} <= PROTECTED_NAMES
    assert {"/admin", "/admin.html", "/login", "/login.html"} <= protected_routes()


def test_stale_static_copies_from_an_earlier_build_are_deleted_because_they_would_shadow_the_pages():
    existing = {"public/about.html", "public/index.html", "public/unrelated.html", "pages/about.vue"}
    plan = plan_conversion(SITE, existing_paths=existing)
    assert plan.files["public/about.html"] is None and plan.files["public/index.html"] is None
    assert "public/unrelated.html" not in plan.files  # only the copies of converted pages
    assert sorted(plan.deleted) == ["public/about.html", "public/index.html"]
    assert "removed older static copies" in plan.appendix


def test_only_paths_that_exist_are_deleted():
    plan = plan_conversion(SITE, existing_paths={"public/about.html"})
    assert plan.deleted == ["public/about.html"] and "public/index.html" not in plan.files


def test_when_the_branch_could_not_be_listed_nothing_is_deleted_and_the_agent_is_told_to():
    plan = plan_conversion(SITE, existing_paths=None)
    assert plan.deleted == [] and not any(c is None for c in plan.files.values())
    assert "delete it: a static file is served before the page" in plan.appendix


def test_a_site_with_no_convertible_page_yields_no_plan():
    assert plan_conversion({"assets/css/a.css": "a{}"}, existing_paths=set()) is None
    assert plan_conversion({"admin.html": "<p>x</p>"}, existing_paths=set()) is None


# ---- what the agent is told ------------------------------------------------------------------------


def test_the_appendix_tells_the_agent_to_keep_the_pages_and_only_add_the_functional_part():
    text = plan_conversion(SITE, existing_paths=set()).appendix
    assert "already converted" in text and "`/about.html`" in text
    assert "`legacy/pages/<name>.html`" in text and "composables/useLegacyScripts.ts" in text
    assert "Do NOT re-template" in text and "do NOT copy the imported HTML into `public/`" in text
    assert "do NOT delete `legacy/`" in text
    assert "append it to the page's `useLegacyScripts([...])` list" in text
    assert "redirects to `/about.html`" in text  # why the clean URL is not the page


def test_a_long_list_of_pages_or_skips_is_capped():
    pages = [{"route": f"/p{i}.html"} for i in range(60)]
    skipped = [{"path": f"s{i}.html", "reason": "r"} for i in range(60)]
    text = build_appendix(pages, skipped, [], True)
    assert "and 35 more" in text and text.count("- `s") == 25


def test_the_summary_is_small_and_has_no_contents():
    summary = plan_conversion(SITE, existing_paths={"public/about.html"}).summary
    assert set(summary) == {"pages", "skipped", "deleted", "files"}
    assert {"source": "about.html", "route": "/about.html"} in summary["pages"]
    assert "<h1>" not in str(summary)
