"""Tests for AFS MCP server tools — direct invocation (no transport)."""

import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

import json
from afs.mcp.server import afs_explore, afs_compile, afs_run

# Same world model used by test_compiler.py
SAMPLE_WORLD_MODEL = {
    "domain": "staffing",
    "problem": {
        "statement": "Optimize coffee shop staffing",
        "restatement": "Find optimal staffing levels for a coffee shop",
        "objectives": ["Minimize cost", "Maximize service quality"],
    },
    "concepts": [
        {
            "name": "labor_cost",
            "displayName": "Labor Cost",
            "description": "Total hourly labor expenditure",
            "nature": "resource",
            "category": "financial",
            "controllability": "direct",
            "dataDictionary": {
                "validRange": [500, 5000],
                "displayRange": [500, 5000],
                "displayUnit": "$/week",
                "unitOfMeasure": "$/week",
            },
        },
        {
            "name": "service_quality",
            "displayName": "Service Quality",
            "description": "Customer satisfaction and wait times",
            "nature": "outcome",
            "category": "human",
            "controllability": "indirect",
            "dataDictionary": {
                "validRange": [0, 100],
                "displayRange": [0, 100],
                "displayUnit": "score",
                "unitOfMeasure": "score",
            },
        },
        {
            "name": "staff_count",
            "displayName": "Staff Count",
            "description": "Number of employees on shift",
            "nature": "resource",
            "category": "operational",
            "controllability": "direct",
            "dataDictionary": {
                "validRange": [2, 20],
                "displayRange": [2, 20],
                "displayUnit": "people",
                "unitOfMeasure": "people",
            },
        },
        {
            "name": "customer_demand",
            "displayName": "Customer Demand",
            "description": "Expected customer volume per hour",
            "nature": "force",
            "category": "operational",
            "controllability": "uncontrollable",
            "dataDictionary": {
                "validRange": [10, 200],
                "displayRange": [10, 200],
                "displayUnit": "customers/hr",
                "unitOfMeasure": "customers/hr",
            },
        },
    ],
    "relationships": [
        {
            "fromConcept": "staff_count",
            "toConcept": "labor_cost",
            "direction": 1,
            "strength": 0.9,
            "nature": "enables",
            "mechanism": "More staff directly increases labor expenditure",
            "responseCurve": {"type": "linear", "truthTable": []},
            "confidence": "high",
            "symmetry": {"isSymmetric": False},
        },
        {
            "fromConcept": "staff_count",
            "toConcept": "service_quality",
            "direction": 1,
            "strength": 0.7,
            "nature": "enables",
            "mechanism": "More staff reduces wait times and improves service",
            "responseCurve": {"type": "logarithmic", "truthTable": []},
            "confidence": "medium",
            "symmetry": {"isSymmetric": False},
        },
        {
            "fromConcept": "labor_cost",
            "toConcept": "service_quality",
            "direction": -1,
            "strength": 0.6,
            "nature": "trades_off",
            "mechanism": "Budget pressure forces cutting staff, reducing quality",
            "responseCurve": {"type": "linear", "truthTable": []},
            "confidence": "medium",
            "symmetry": {"isSymmetric": False},
        },
        {
            "fromConcept": "customer_demand",
            "toConcept": "service_quality",
            "direction": -1,
            "strength": 0.8,
            "nature": "constrains",
            "mechanism": "Higher demand with same staff degrades service",
            "responseCurve": {"type": "linear", "truthTable": []},
            "confidence": "high",
            "symmetry": {"isSymmetric": False},
        },
    ],
    "tradeoffs": [
        {"concepts": ["labor_cost", "service_quality"], "description": "Cost vs quality", "severity": "severe"},
    ],
    "referencePoints": [
        {
            "name": "Budget Operation",
            "description": "Minimum staffing",
            "position": {"labor_cost": 1000, "service_quality": 30, "staff_count": 3, "customer_demand": 50},
            "source": "industry data",
        },
        {
            "name": "Premium Operation",
            "description": "Full staffing",
            "position": {"labor_cost": 4000, "service_quality": 90, "staff_count": 15, "customer_demand": 150},
            "source": "industry data",
        },
    ],
    "attractors": [
        {
            "name": "Cost Minimizer",
            "description": "Bare minimum staffing",
            "position": {"labor_cost": 800, "service_quality": 25, "staff_count": 2, "customer_demand": 50},
            "strength": 0.7,
        },
        {
            "name": "Quality Leader",
            "description": "Premium service experience",
            "position": {"labor_cost": 4500, "service_quality": 95, "staff_count": 18, "customer_demand": 150},
            "strength": 0.8,
        },
        {
            "name": "Balanced",
            "description": "Mid-range efficiency",
            "position": {"labor_cost": 2500, "service_quality": 70, "staff_count": 10, "customer_demand": 100},
            "strength": 0.9,
        },
    ],
    "confidence": {"overall": "medium", "weakestLink": "customer_demand", "dataGaps": []},
    "validation": {"valid": True, "errors": [], "warnings": []},
}


def test_afs_compile():
    """afs-compile should return field, config, and influence matrix."""
    print("test_afs_compile...", end=" ")

    result = afs_compile(SAMPLE_WORLD_MODEL)

    assert "field" in result, "Missing 'field' in result"
    assert "config" in result, "Missing 'config' in result"
    assert "influence_matrix" in result, "Missing 'influence_matrix'"
    assert "calibration" in result, "Missing 'calibration'"

    # Field should have 4 active dimensions
    field = result["field"]
    active_dims = [d for d in field.get("dimensions", []) if d.get("lifecycle_state") == "active"]
    assert len(active_dims) == 4, f"Expected 4 active dims, got {len(active_dims)}"

    # Influence matrix should be 4x4
    matrix = result["influence_matrix"]
    assert len(matrix) == 4, f"Expected 4 rows, got {len(matrix)}"
    assert len(matrix[0]) == 4, f"Expected 4 cols, got {len(matrix[0])}"

    # Config should be a dict with engine params
    config = result["config"]
    assert "engine" in config, "Config missing 'engine' section"

    print("PASS")
    return result


def test_afs_run(compile_result):
    """afs-run should produce clusters from a pre-compiled field."""
    print("test_afs_run...", end=" ")

    result = afs_run(
        field=compile_result["field"],
        config=compile_result["config"],
        generations=5,
    )

    assert "clusters" in result, "Missing 'clusters'"
    assert "final_population" in result, "Missing 'final_population'"
    assert "generations" in result, "Missing 'generations'"
    assert result["generations"] == 5
    assert result["final_population"] > 0, "Colony died — 0 cells"

    # Should have concept names
    assert "concept_names" in result
    # With attractors: concepts = attractors (3). Without: concepts = dimensions (4).
    assert len(result["concept_names"]) >= 3

    print(f"PASS (pop={result['final_population']}, clusters={len(result['clusters'])})")
    return result


def test_afs_explore():
    """afs-explore should run the full pipeline end-to-end."""
    print("test_afs_explore...", end=" ")

    result = afs_explore(SAMPLE_WORLD_MODEL, generations=5)

    assert "clusters" in result, "Missing 'clusters'"
    assert "field" in result, "Missing 'field'"
    assert "influence_matrix" in result, "Missing 'influence_matrix'"
    assert "calibration" in result, "Missing 'calibration'"
    assert "final_population" in result, "Missing 'final_population'"
    assert result["final_population"] > 0, "Colony died"

    # Clusters should have concept weights
    if len(result["clusters"]) > 0:
        cl = result["clusters"][0]
        assert "concept_weights" in cl, "Cluster missing concept_weights"
        assert "cell_count" in cl, "Cluster missing cell_count"

    print(f"PASS (pop={result['final_population']}, clusters={len(result['clusters'])})")
    return result


def test_afs_explore_results_are_json_serializable():
    """All results must be JSON-serializable for MCP transport."""
    print("test_afs_explore_results_are_json_serializable...", end=" ")

    result = afs_explore(SAMPLE_WORLD_MODEL, generations=3)
    serialized = json.dumps(result)
    assert len(serialized) > 100, "Suspiciously small JSON output"

    # Round-trip
    parsed = json.loads(serialized)
    assert parsed["generations"] == 3

    print("PASS")


if __name__ == "__main__":
    passed = 0
    failed = 0

    try:
        compile_result = test_afs_compile()
        passed += 1
    except Exception as e:
        print(f"FAIL: {e}")
        failed += 1
        compile_result = None

    if compile_result:
        try:
            test_afs_run(compile_result)
            passed += 1
        except Exception as e:
            print(f"FAIL: {e}")
            failed += 1

    try:
        test_afs_explore()
        passed += 1
    except Exception as e:
        print(f"FAIL: {e}")
        failed += 1

    try:
        test_afs_explore_results_are_json_serializable()
        passed += 1
    except Exception as e:
        print(f"FAIL: {e}")
        failed += 1

    print(f"\n{'='*40}")
    print(f"Results: {passed} passed, {failed} failed")
    if failed > 0:
        sys.exit(1)
