"""End-to-end persistence of the output_language settings change.

Closes the loop the reviewer flagged: a language chosen in the settings web UI
must (1) take effect immediately for the reply engine and (2) survive a
container recreate. The pieces:

  bridge._save()        -> writes BOTH /data/jarvis-settings.json (persistent)
                           and JARVIS_CONFIG_PATH (live runtime config)
  entrypoint merge      -> on recreate, re-renders config from the env template
                           then merges the persistent overrides back on top
  engine._resolve_output_language() -> reads JARVIS_CONFIG_PATH, config wins
                           over the OUTPUT_LANGUAGE env

This test drives the REAL bridge save function and the REAL engine resolver
(the resolver is loaded standalone because the full engine import needs the
mcp package, which isn't installed in CI here). It simulates the env default
disagreeing with the chosen language, which is exactly the bug condition.
"""

import ast
import json
import os
from pathlib import Path

import pytest

# bridge.settings_web imports only stdlib at module load (flask is imported
# lazily inside register()), so it is safe to import directly.
import sys

sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "bridge"))
import settings_web  # noqa: E402


def _load_resolver():
    """Load engine._resolve_output_language + _extra_config without importing
    the heavy jarvis package (which pulls in the optional mcp dependency)."""
    src = (
        Path(__file__).resolve().parents[1]
        / "src/jarvis/reply/engine.py"
    ).read_text("utf-8")
    tree = ast.parse(src)
    wanted = {"_extra_config", "_resolve_output_language"}
    mod = ast.Module(
        body=[
            n
            for n in tree.body
            if isinstance(n, ast.FunctionDef) and n.name in wanted
        ],
        type_ignores=[],
    )
    ns = {"os": os, "Optional": __import__("typing").Optional}
    exec(compile(mod, "engine_subset", "exec"), ns)  # noqa: S102
    return ns["_resolve_output_language"]


def _simulate_recreate_merge(template_lang: str, config_path: Path, persist_path: Path):
    """Mirror docker/entrypoint.sh: re-render the runtime config from the env
    template, then merge the persistent overrides on top."""
    config_path.write_text(json.dumps({"output_language": template_lang}), "utf-8")
    if persist_path.exists():
        base = json.loads(config_path.read_text("utf-8"))
        ov = json.loads(persist_path.read_text("utf-8"))
        base.update(ov)
        config_path.write_text(json.dumps(base, ensure_ascii=False, indent=2), "utf-8")


@pytest.mark.integration
def test_settings_save_applies_and_survives_recreate(monkeypatch, tmp_path):
    config_path = tmp_path / "jarvis.json"
    persist_path = tmp_path / "data" / "jarvis-settings.json"
    # The compose env default is the "old" language that must be overridden.
    monkeypatch.setenv("OUTPUT_LANGUAGE", "English")
    monkeypatch.setenv("JARVIS_CONFIG_PATH", str(config_path))
    monkeypatch.setenv("JARVIS_SETTINGS_PATH", str(persist_path))
    # Start from the env-rendered config (as entrypoint would produce).
    config_path.write_text(json.dumps({"output_language": "English"}), "utf-8")

    resolve = _load_resolver()
    # Before the change: the env default wins.
    assert resolve() == "English"

    # 1) User saves Korean in the settings UI.
    settings_web._save({"output_language": "Korean"})

    # Both targets are written.
    assert json.loads(config_path.read_text("utf-8"))["output_language"] == "Korean"
    assert json.loads(persist_path.read_text("utf-8"))["output_language"] == "Korean"

    # 2) Applies immediately: the resolver now returns Korean (config > env).
    assert resolve() == "Korean"

    # 3) Survives a container recreate: entrypoint re-renders the config from the
    #    env template (still English) then merges the persistent override.
    _simulate_recreate_merge("English", config_path, persist_path)
    assert json.loads(config_path.read_text("utf-8"))["output_language"] == "Korean"
    assert resolve() == "Korean"


@pytest.mark.integration
def test_persona_and_directive_follow_persisted_language(monkeypatch, tmp_path):
    """After persistence, the persona and the reply directive both lock to the
    saved language, not the env default."""
    from jarvis.system_prompt import build_system_prompt, reply_language_directive

    config_path = tmp_path / "jarvis.json"
    persist_path = tmp_path / "data" / "jarvis-settings.json"
    monkeypatch.setenv("OUTPUT_LANGUAGE", "English")
    monkeypatch.setenv("JARVIS_CONFIG_PATH", str(config_path))
    monkeypatch.setenv("JARVIS_SETTINGS_PATH", str(persist_path))
    config_path.write_text(json.dumps({"output_language": "English"}), "utf-8")

    settings_web._save({"output_language": "Korean"})
    lang = _load_resolver()()

    persona = build_system_prompt("Jarvis", lang)
    directive = reply_language_directive(lang, "melo")
    assert "in Korean" in persona and "in English" not in persona
    assert directive is not None and "Korean" in directive
