"""Chaos: Spec-Level Failure.

Proving: "Corrupted intent is always rejected — within the validator's scope."

NOTE: The current Rust validator checks api_version, identity_version, entity,
threshold ordering, weight bounds, and duplicate rule names. It is permissive
about missing sections (rules, sources, blocking, decision) and unknown rule
types. Tests document both validated and unvalidated boundaries.
"""
from __future__ import annotations

import pytest
import yaml

from tests.contract.conftest import MINIMAL_VALID_SPEC, make_spec, requires_native

pytestmark = requires_native

from kanoniv.validate import validate


# ---------------------------------------------------------------------------
# Structural corruption — test what IS and IS NOT validated
# ---------------------------------------------------------------------------


class TestStructuralCorruption:
    def test_drop_entity_fails(self):
        """Dropping entity → validation error (entity IS required)."""
        parsed = yaml.safe_load(MINIMAL_VALID_SPEC)
        parsed.pop("entity", None)
        broken_yaml = yaml.dump(parsed, default_flow_style=False)
        spec = make_spec(broken_yaml)
        result = validate(spec)
        assert not result.valid

    def test_drop_api_version_fails(self):
        """Dropping api_version → validation error."""
        parsed = yaml.safe_load(MINIMAL_VALID_SPEC)
        parsed.pop("api_version", None)
        broken_yaml = yaml.dump(parsed, default_flow_style=False)
        spec = make_spec(broken_yaml)
        result = validate(spec)
        assert not result.valid

    def test_drop_identity_version_fails(self):
        """Dropping identity_version → validation error."""
        parsed = yaml.safe_load(MINIMAL_VALID_SPEC)
        parsed.pop("identity_version", None)
        broken_yaml = yaml.dump(parsed, default_flow_style=False)
        spec = make_spec(broken_yaml)
        result = validate(spec)
        assert not result.valid

    @pytest.mark.parametrize("key", ["sources", "rules", "decision", "blocking"])
    def test_drop_optional_key_accepted(self, key):
        """Dropping sources/rules/decision/blocking → currently accepted (permissive)."""
        parsed = yaml.safe_load(MINIMAL_VALID_SPEC)
        parsed.pop(key, None)
        broken_yaml = yaml.dump(parsed, default_flow_style=False)
        spec = make_spec(broken_yaml)
        result = validate(spec)
        # Document: validator is permissive about these sections
        assert result.valid


# ---------------------------------------------------------------------------
# Type confusion
# ---------------------------------------------------------------------------


class TestTypeConfusion:
    def test_wrong_type_for_threshold_accepted(self):
        """match: 'high' (string) → currently accepted (validator doesn't type-check thresholds strictly)."""
        modified = MINIMAL_VALID_SPEC.replace("match: 0.7", 'match: "high"')
        spec = make_spec(modified)
        result = validate(spec)
        # Document behavior: string thresholds are currently accepted
        assert isinstance(result.valid, bool)

    def test_wrong_type_for_weight_accepted(self):
        """weight: true → currently accepted (validator doesn't type-check weight types strictly)."""
        modified = MINIMAL_VALID_SPEC.replace("weight: 1.0", "weight: true")
        spec = make_spec(modified)
        result = validate(spec)
        assert isinstance(result.valid, bool)

    def test_extra_unknown_keys_accepted_or_rejected(self):
        """Spec with unknown keys → consistent behavior (not crash)."""
        modified = MINIMAL_VALID_SPEC + "unknown_key: some_value\n"
        spec = make_spec(modified)
        result = validate(spec)
        assert isinstance(result.valid, bool)


# ---------------------------------------------------------------------------
# Incomplete rules — what IS and IS NOT validated
# ---------------------------------------------------------------------------


class TestIncompleteRules:
    def test_rule_missing_name(self):
        """Rule without name: → rejected (names required for dedup check)."""
        yaml_str = """\
api_version: kanoniv/v2
identity_version: test_v1.0
entity:
  name: customer
sources:
  - name: src_a
    system: csv
    table: src_a
    id: id
    attributes:
      email: email
blocking:
  strategy: composite
  keys:
    - [email]
rules:
  - type: exact
    field: email
    weight: 1.0
decision:
  thresholds:
    match: 0.7
    review: 0.4
  conflict_strategy: prefer_high_confidence
"""
        spec = make_spec(yaml_str)
        result = validate(spec)
        assert not result.valid

    def test_rule_missing_type(self):
        """Rule without type: → rejected."""
        yaml_str = """\
api_version: kanoniv/v2
identity_version: test_v1.0
entity:
  name: customer
sources:
  - name: src_a
    system: csv
    table: src_a
    id: id
    attributes:
      email: email
blocking:
  strategy: composite
  keys:
    - [email]
rules:
  - name: email_match
    field: email
    weight: 1.0
decision:
  thresholds:
    match: 0.7
    review: 0.4
  conflict_strategy: prefer_high_confidence
"""
        spec = make_spec(yaml_str)
        result = validate(spec)
        assert not result.valid

    def test_similarity_rule_without_algorithm_accepted(self):
        """Similarity rule without algorithm → currently accepted (permissive)."""
        yaml_str = """\
api_version: kanoniv/v2
identity_version: test_v1.0
entity:
  name: customer
sources:
  - name: src_a
    system: csv
    table: src_a
    id: id
    attributes:
      name: name
blocking:
  strategy: composite
  keys:
    - [name]
rules:
  - name: name_sim
    type: similarity
    field: name
    threshold: 0.8
    weight: 1.0
decision:
  thresholds:
    match: 0.7
    review: 0.4
  conflict_strategy: prefer_high_confidence
"""
        spec = make_spec(yaml_str)
        result = validate(spec)
        # Document: missing algorithm is currently accepted
        assert result.valid

    def test_similarity_rule_without_threshold_accepted(self):
        """Similarity rule without threshold → currently accepted (permissive)."""
        yaml_str = """\
api_version: kanoniv/v2
identity_version: test_v1.0
entity:
  name: customer
sources:
  - name: src_a
    system: csv
    table: src_a
    id: id
    attributes:
      name: name
blocking:
  strategy: composite
  keys:
    - [name]
rules:
  - name: name_sim
    type: similarity
    field: name
    algorithm: jaro_winkler
    weight: 1.0
decision:
  thresholds:
    match: 0.7
    review: 0.4
  conflict_strategy: prefer_high_confidence
"""
        spec = make_spec(yaml_str)
        result = validate(spec)
        assert result.valid
