"""Regression tests — pin exact encode/decode outputs.

These tests lock down the current behavior for key inputs. If a refactor
changes the output, the test will fail and force a conscious decision
about whether the change is intentional.

Update the expected values ONLY when a change is deliberate.
"""

import pytest
from cdn import encode, decode


# ── Encode regression: NL → exact Condensa output ────────────────────

class TestEncodeRegression:
    """Pin exact encoder outputs for known inputs."""

    def test_search_basic(self):
        result = encode("Search the web for SpaceX news")
        assert "srch" in result
        assert "SpaceX" in result  # content preserved

    def test_filter_sort_pipeline(self):
        result = encode("Filter active users and sort descending")
        assert "filt" in result
        assert "/desc" in result

    def test_error_429(self):
        assert encode("Error: the API returned a 429") == "E: 429"

    def test_error_timeout_with_retry(self):
        result = encode("Error: timeout occurred, retry 3 retries")
        assert result == "E: timeout retry:3"

    def test_cancel_all_discard(self):
        assert encode("Cancel all running tasks and discard partial results") == "X: all discard:T"

    def test_status_complete(self):
        assert encode("Processing complete. 4 out of 4 stages done.") == "#: done 4/4"

    def test_urgency_critical(self):
        result = encode("CRITICAL: drop everything and execute shutdown")
        assert result.startswith("+++")
        assert "exec" in result
        assert "shutdown" in result  # content preserved

    def test_acknowledged(self):
        assert encode("Acknowledged") == "<:ack"

    def test_got_it(self):
        assert encode("Got it") == "<:ack"

    def test_delegate(self):
        result = encode("Delegate to AgentC: review code")
        assert result.startswith(">:@AgentC")
        assert "review" in result
        assert "code" in result  # content preserved

    def test_policy_retry(self):
        assert encode("Set default retry to 3 for all messages") == "@:policy retry:3"

    def test_wire(self):
        assert encode("Wire dashboard to programs") == "!:wire dashboard -> programs"

    def test_assemble_bare(self):
        assert encode("Assemble") == "!?:asm"

    def test_pipeline_and_then(self):
        assert encode("Search for X and then summarize") == "!: srch | sumz"

    def test_context_update(self):
        assert encode("Language changed from English to Spanish") == "~:& language:English->Spanish"

    def test_result_answer(self):
        assert encode("The answer is 42") == "=: 42"

    def test_correction(self):
        result = encode("Revision: section 1: keep, section 2: revise, section 3: remove")
        assert result == "~:^ s1:keep s2:revise s3:del"


# ── Decode regression: exact Condensa → exact NL output ──────────────

class TestDecodeRegression:
    """Pin exact decoder outputs for known Condensa inputs."""

    def test_command_search(self):
        assert decode("!:srch 'SpaceX' /n:5") == "Search SpaceX. Limit to 5 results."

    def test_command_with_fallback(self):
        assert decode("!:read data fb:cache") == "Read data. On failure: use cached data."

    def test_sync_command(self):
        assert decode("!?:read user_profile /timeout:10s fb:cache") == \
            "Synchronously read user_profile. Timeout: 10s. On failure: use cached data."

    def test_sync_command_v02_compat(self):
        assert decode("!!:read user_profile /timeout:10s") == \
            "Synchronously read user_profile. Timeout: 10s."

    def test_error(self):
        result = decode("E:429 retry:3 wait:30s backoff:exp fb:degrade")
        assert "429" in result
        assert "retry" in result.lower()
        assert "exponential" in result.lower()

    def test_status_done(self):
        assert decode("#:done 4/4 records:12847 fail:0") == \
            "Status: completed. Progress: 4/4. Records: 12847. Fail: 0."

    def test_cancel(self):
        assert decode("X: all discard:T") == "Cancel all. Discard partial results."

    def test_pipeline(self):
        assert decode("!:filt status:active | grp region | avg revenue") == \
            "Pipeline: Filter status=active. → Group_by region. → Average revenue."

    def test_urgency_critical(self):
        assert decode("+++!:exec shutdown") == "[CRITICAL] Execute shutdown."

    def test_urgency_background(self):
        assert decode("--!:exec cleanup") == "[BACKGROUND] Execute cleanup."

    def test_update_inherit(self):
        assert decode("~:^ s1:keep s2:revise+detail s3:del") == \
            "Inherit and modify context: s1: keep as-is, s2: revise with more detail, s3: remove."

    def test_meta_define(self):
        assert decode("@:define QRS 'read|filt|grp|agg'") == \
            "Define shorthand: QRS 'read|filt|grp|agg'"

    def test_meta_ack(self):
        assert decode("@:ack") == "Acknowledged."

    def test_meta_verbose(self):
        assert decode("@:verbose") == "Enable verbose mode."

    def test_delegate(self):
        assert decode(">:@AgentC review code") == "Delegate to @AgentC: Review code."

    def test_result_with_confidence(self):
        assert decode("=:42 !") == "Result: 42 (confidence: certain)."

    def test_result_uncertain(self):
        assert decode("=:maybe ~~") == "Result: maybe (confidence: uncertain)."

    def test_policy(self):
        assert decode("@:policy retry:3 timeout:30s fb:degrade") == \
            "Set session defaults: retry=3, timeout=30s, fb=degrade"

    def test_each_modifier(self):
        assert decode("!:sumz results /each") == "Summarize results. Apply to each."

    def test_scan_modifier(self):
        assert decode("!:sum values /scan") == "Sum values. Apply cumulatively."

    def test_repeat_modifier(self):
        assert decode("!:exec task /repeat:5") == "Execute task. Repeat 5 times."

    def test_implicit_var_dollar_underscore(self):
        assert decode("!:sumz $_ /n:200") == \
            "Summarize the previous result. Limit to 200 results."

    def test_bulk_literal(self):
        assert decode('!:wrt <<<{"key": "value"}>>>') == 'Write {"key": "value"}.'

    def test_safe_access(self):
        assert decode("!:read metadata?") == "Read metadata (if exists)."

    def test_seq_flow(self):
        result = decode("seq(!:read data; !:filt active)")
        assert "sequential" in result.lower()
        assert "Step 1" in result
        assert "Step 2" in result

    def test_par_flow(self):
        result = decode("par(!:xlat /lang:es; !:sumz /n:200)")
        assert "parallel" in result.lower()


# ── Condensa Code regression ─────────────────────────────────────────

class TestCondensaCodeRegression:
    """Pin Condensa Code decode outputs."""

    def test_fn_decode(self):
        result = decode("!:fn DashboardPage /props:(programs:Program[] onSelect:fn)")
        assert "function" in result.lower() or "define" in result.lower()
        assert "DashboardPage" in result

    def test_type_decode(self):
        result = decode("!:type Program { id:n name:s* weeks:n }")
        assert "type" in result.lower()
        assert "Program" in result

    def test_wire_decode(self):
        result = decode("!:wire dashboard.onSelect -> programs.highlight")
        assert "wire" in result.lower() or "Wire" in result
        assert "dashboard" in result

    def test_api_decode(self):
        result = decode("!:api POST /users /body:CreateUser /returns:User")
        assert "api" in result.lower()
        assert "POST" in result

    def test_schema_decode(self):
        result = decode("!:schema workouts { id:n pk user_id:n fk:users }")
        assert "schema" in result.lower()
        assert "workouts" in result

    def test_asm_decode(self):
        result = decode("!?:asm")
        assert "assemble" in result.lower()


# ── Round-trip stability regression ──────────────────────────────────

class TestRoundTripRegression:
    """Verify that encode→decode preserves key semantic tokens."""

    @pytest.mark.parametrize("nl_input,expected_tokens", [
        ("Search for SpaceX news, top 5 results", ["search", "5"]),
        ("Filter active users and sort descending", ["filter", "descending"]),
        ("Error: the API returned a 429", ["429"]),
        ("Cancel all tasks", ["cancel"]),
        ("Translate to Spanish", ["translate", "es"]),
        ("Summarize in bullets", ["summar", "bullet"]),
    ])
    def test_semantic_preservation(self, nl_input, expected_tokens):
        """Encode then decode — key meaning tokens should survive."""
        encoded = encode(nl_input)
        decoded = decode(encoded)
        decoded_lower = decoded.lower()
        for token in expected_tokens:
            assert token in decoded_lower, \
                f"Token '{token}' lost in round-trip: '{nl_input}' → '{encoded}' → '{decoded}'"
