"""8D: HTML-escape runtime vars in html_body only. Cloud owns render escaping."""
from __future__ import annotations

import html
import re
from pathlib import Path

import pytest

from src.domain.codegen.email_templates_manifest import OVERLAY_SYNC_CONTRACT
from src.domain.connectors.invoke_capabilities import CAPABILITY_CONVERSION_PATTERN, FORMS_FAMILY_PLAN
from src.domain.connectors.schemas import InvokeEmailSendRequest
from src.domain.forms.email import FORBIDDEN_EMAIL_CONSTANT_KEYS, FORMS_EMAIL_COMPOSITION

BE_ROOT = Path(__file__).resolve().parents[1]
CLOUD_LIB = BE_ROOT.parent / "P2HSCloud" / "apps" / "pc-gateway" / "src" / "lib"
CLOUD_HTML = CLOUD_LIB / "email-template-html.ts"
CLOUD_RENDER = CLOUD_LIB / "email-templates.ts"
CLOUD_VALIDATE = CLOUD_LIB / "email-template-validate.ts"
AUTH_TS = BE_ROOT.parent / "P2HSCloud" / "apps" / "pc-gateway" / "src" / "routes" / "auth.ts"
NOTIFY_YAML = BE_ROOT / "seeds" / "function_library" / "contact-capture-notify.yaml"
CORE_MANIFEST = BE_ROOT / "src" / "domain" / "codegen" / "email_templates_manifest.py"

CLOSED_KEYS = (
    "auth.verify_email",
    "auth.otp_request",
    "auth.magic_link",
    "auth.password_reset",
    "auth.invite",
    "contact_capture.admin_notify",
    "contact_capture.confirmation",
)

HOSTILE = (
    "<script>alert(1)</script>",
    "<img src=x onerror=alert(1)>",
    '"><script>alert(1)</script>',
    "Tom & Jerry",
    '"quoted"',
    "'apostrophe'",
    '<a href="https://evil.example">click</a>',
)

TOKEN = re.compile(r"\{\{\s*([a-z_]+)\s*\}\}", re.I)

CATALOG_HTML_MATRIX = {
    "auth.verify_email": {
        "has_html_default": True,
        "runtime_vars_used_in_html": True,
        "attribute_context_vars": ("action_url",),
        "text_context_vars": (),
    },
    "auth.otp_request": {
        "has_html_default": True,
        "runtime_vars_used_in_html": True,
        "attribute_context_vars": (),
        "text_context_vars": ("code",),
    },
    "auth.magic_link": {
        "has_html_default": True,
        "runtime_vars_used_in_html": True,
        "attribute_context_vars": ("action_url",),
        "text_context_vars": (),
    },
    "auth.password_reset": {
        "has_html_default": True,
        "runtime_vars_used_in_html": True,
        "attribute_context_vars": ("action_url",),
        "text_context_vars": (),
    },
    "auth.invite": {
        "has_html_default": True,
        "runtime_vars_used_in_html": True,
        "attribute_context_vars": ("action_url",),
        "text_context_vars": (),
    },
    "contact_capture.admin_notify": {
        "has_html_default": True,
        "runtime_vars_used_in_html": True,
        "attribute_context_vars": (),
        "text_context_vars": ("name", "email", "message"),
    },
    "contact_capture.confirmation": {
        "has_html_default": True,
        "runtime_vars_used_in_html": True,
        "attribute_context_vars": (),
        "text_context_vars": ("name",),
    },
}


def escape_html(value: str) -> str:
    return html.escape(value, quote=True)


def substitute_text(template: str, vars_map: dict[str, str]) -> str:
    def repl(match: re.Match[str]) -> str:
        name = match.group(1)
        return vars_map[name] if name in vars_map else match.group(0)

    return TOKEN.sub(repl, template)


def substitute_html(template: str, vars_map: dict[str, str]) -> str:
    def repl(match: re.Match[str]) -> str:
        name = match.group(1)
        return escape_html(vars_map[name]) if name in vars_map else match.group(0)

    return TOKEN.sub(repl, template)


def test_cloud_escape_helper_is_minimal_html_encoding() -> None:
    src = CLOUD_HTML.read_text(encoding="utf-8")
    assert 'replace(/&/g, "&amp;")' in src
    assert 'replace(/</g, "&lt;")' in src
    assert 'replace(/>/g, "&gt;")' in src
    assert 'replace(/"/g, "&quot;")' in src
    assert "&#x27;" in src
    assert "sanitize" not in src.lower()
    assert "DOMParser" not in src
    assert escape_html("&<>\"'") == "&amp;&lt;&gt;&quot;&#x27;"


def test_cloud_render_splits_text_and_html_substitution() -> None:
    render = CLOUD_RENDER.read_text(encoding="utf-8")
    assert "substituteText(override.subject, resolved)" in render
    assert "substituteText(override.text_body, resolved)" in render
    assert "substituteHtml(override.html_body, resolved)" in render
    assert "html: def.defaultHtml(resolved)" in render
    assert "RawHtml" not in render
    assert "SafeHtml" not in render
    assert "trusted_html" not in render


def test_defaults_escape_interpolated_html_vars() -> None:
    render = CLOUD_RENDER.read_text(encoding="utf-8")
    for key in CLOSED_KEYS:
        assert f'"{key}"' in render
    assert render.count("escapeHtml(") >= 9
    assert "href=\"${escapeHtml(v.action_url" in render
    assert "escapeHtml(v.code" in render
    assert "escapeHtml(v.name" in render
    assert "escapeHtml(v.email" in render
    assert "escapeHtml(v.message" in render
    assert "defaultText: (v) => `Confirm your email for ${v.app_name}" in render
    assert "defaultSubject: (v) => `Confirm your email for ${v.app_name}`" in render


def test_catalog_html_interpolation_matrix() -> None:
    assert tuple(CATALOG_HTML_MATRIX) == CLOSED_KEYS
    for spec in CATALOG_HTML_MATRIX.values():
        assert spec["has_html_default"] is True
        assert spec["runtime_vars_used_in_html"] is True


@pytest.mark.parametrize("value", HOSTILE)
def test_hostile_values_in_subject_and_text_stay_raw(value: str) -> None:
    assert substitute_text("Hello {{name}}", {"name": value}) == f"Hello {value}"
    assert substitute_text("Body {{name}}", {"name": value}) == f"Body {value}"


@pytest.mark.parametrize("value", HOSTILE)
def test_hostile_values_in_html_text_node_are_escaped(value: str) -> None:
    out = substitute_html("<p>{{message}}</p>", {"message": value})
    assert out == f"<p>{escape_html(value)}</p>"
    assert "<script>" not in out
    assert "<img " not in out


@pytest.mark.parametrize("value", HOSTILE)
def test_hostile_values_in_quoted_html_attribute_are_escaped(value: str) -> None:
    out = substitute_html('<a href="{{action_url}}">go</a>', {"action_url": value})
    assert out == f'<a href="{escape_html(value)}">go</a>'
    assert 'href="<script' not in out
    assert 'href=""><script' not in out


def test_auth_url_ampersand_is_html_escaped_not_url_encoded() -> None:
    url = "https://example.com/verify?token=abc&next=/dashboard"
    html_out = substitute_html('<a href="{{action_url}}">Verify email</a>', {"action_url": url})
    assert "&amp;" in html_out
    assert "%26" not in html_out
    assert html_out == f'<a href="{escape_html(url)}">Verify email</a>'
    text_out = substitute_text("Verify: {{action_url}}", {"action_url": url})
    assert text_out == f"Verify: {url}"


def test_auth_url_quote_and_fragment_remain_semantically_correct() -> None:
    quoted = 'https://example.com/reset?q="x"'
    hashed = "https://example.com/a?x=1&y=2#section"
    html_q = substitute_html('<a href="{{action_url}}">x</a>', {"action_url": quoted})
    html_h = substitute_html('<a href="{{action_url}}">x</a>', {"action_url": hashed})
    assert html_q == f'<a href="{escape_html(quoted)}">x</a>'
    assert "&amp;y=2#section" in html_h
    assert "#section" in html_h
    assert html.unescape(html_h.split('href="')[1].split('"')[0]) == hashed


def test_otp_code_unchanged_except_html_entities() -> None:
    for code in ("123456", "ABC-123"):
        assert substitute_text("Your {{code}}", {"code": code}) == f"Your {code}"
        assert substitute_html("<strong>{{code}}</strong>", {"code": code}) == f"<strong>{code}</strong>"


def test_contact_capture_hostile_inputs_are_inert_html() -> None:
    payload = {
        "name": "<script>alert(1)</script>",
        "email": 'x@y.com"><img src=x onerror=alert(1)>',
        "message": "<img src=x onerror=alert(1)>",
    }
    html_out = substitute_html(
        "<p>Name: {{name}}</p><p>Email: {{email}}</p><p>{{message}}</p>",
        payload,
    )
    assert "<script>" not in html_out
    assert "<img " not in html_out
    assert "&lt;img src=x onerror=alert(1)&gt;" in html_out
    assert "&lt;script&gt;alert(1)&lt;/script&gt;" in html_out
    text_out = substitute_text("{{name}}\n{{email}}\n{{message}}", payload)
    assert text_out == f"{payload['name']}\n{payload['email']}\n{payload['message']}"


def test_override_coverage_cases() -> None:
    vars_map = {"name": "Ada & Bo", "code": "99"}
    subject_only = substitute_text("Hi {{name}}", vars_map)
    assert subject_only == "Hi Ada & Bo"
    both = substitute_html("<p>{{name}} {{name}} {{code}}</p>", vars_map)
    assert both == "<p>Ada &amp; Bo Ada &amp; Bo 99</p>"
    unknown = substitute_html("<p>{{unknown}}</p>", vars_map)
    assert unknown == "<p>{{unknown}}</p>"
    missing = substitute_html("<p>{{missing}}</p>", {})
    assert missing == "<p>{{missing}}</p>"


def test_missing_variable_token_is_not_entity_encoded() -> None:
    assert substitute_html("<p>{{missing}}</p>", {"name": "Ada"}) == "<p>{{missing}}</p>"
    assert "&amp;" not in substitute_html("{{missing}}", {})


def test_protected_placeholder_validation_remains_on_source() -> None:
    validate = CLOUD_VALIDATE.read_text(encoding="utf-8")
    assert "overrideSourcePreservesPlaceholders" in validate
    assert "text_body" in validate
    assert "html_body" in validate
    assert "substituteHtml" not in validate


def test_no_double_escape_on_raw_ampersand() -> None:
    once = escape_html("Tom & Jerry")
    assert once == "Tom &amp; Jerry"
    assert "&amp;amp;" not in once
    html_out = substitute_html("<p>{{name}}</p>", {"name": "Tom & Jerry"})
    assert html_out == "<p>Tom &amp; Jerry</p>"


def test_callers_pass_raw_semantic_strings() -> None:
    auth = AUTH_TS.read_text(encoding="utf-8")
    assert "renderAuthEmailTemplate(env, projectId, \"auth.verify_email\", { action_url: verifyUrl })" in auth
    assert "renderAuthEmailTemplate(env, projectId, \"auth.otp_request\", { code })" in auth
    assert "renderAuthEmailTemplate(env, projectId, \"auth.magic_link\", { action_url: magicUrl })" in auth
    assert "renderAuthEmailTemplate(env, projectId, \"auth.password_reset\", { action_url: resetUrl })" in auth
    assert "renderAuthEmailTemplate(env, projectId, \"auth.invite\", { action_url: inviteUrl })" in auth
    notify = NOTIFY_YAML.read_text(encoding="utf-8")
    assert "name: submission.name || ''" in notify
    assert "html.escape" not in notify
    assert "escapeHtml" not in notify
    core = CORE_MANIFEST.read_text(encoding="utf-8")
    assert "def escape" not in core
    assert "html.escape" not in core


def test_unicode_and_newlines_are_not_reformatted() -> None:
    value = "Mohsin — کراچی — café — ✓\nnext"
    assert substitute_text("{{n}}", {"n": value}) == value
    html_out = substitute_html("<p>{{n}}</p>", {"n": value})
    assert "کراچی" in html_out
    assert "café" in html_out
    assert "✓" in html_out
    assert "<br>" not in html_out
    assert "\n" in html_out


def test_empty_and_long_strings_do_not_fail() -> None:
    assert substitute_html("<p>{{n}}</p>", {"n": ""}) == "<p></p>"
    long = "<" * 10_000
    out = substitute_html("{{n}}", {"n": long})
    assert out == escape_html(long)
    assert "<" not in out


def test_email_send_and_forms_boundaries_unchanged() -> None:
    assert set(InvokeEmailSendRequest.model_fields) == {
        "to",
        "subject",
        "text",
        "html",
        "reply_to",
        "idempotency_key",
    }
    assert "template_key" not in InvokeEmailSendRequest.model_fields
    assert FORMS_EMAIL_COMPOSITION["email_templates"] is False
    assert FORMS_EMAIL_COMPOSITION["interpolation"] is False
    assert "template_key" in FORBIDDEN_EMAIL_CONSTANT_KEYS
    assert OVERLAY_SYNC_CONTRACT == "replace_all"


def test_tracker_advances_past_8d() -> None:
    assert CAPABILITY_CONVERSION_PATTERN["phase_8c_status"] == "complete"
    assert CAPABILITY_CONVERSION_PATTERN["phase_8d_status"] == "complete"
    assert CAPABILITY_CONVERSION_PATTERN["phase_8e_status"] == "complete"
    assert CAPABILITY_CONVERSION_PATTERN["next_program_slice"] == "9_app_block_modernization"
    assert FORMS_FAMILY_PLAN["next_slice"] == "9_app_block_modernization"
    assert CAPABILITY_CONVERSION_PATTERN["phase_7_status"] == "complete"
