"""Tests for Homeostasis Calibrator — verify auto-tuning produces valid configs."""

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

import numpy as np
from afs.core import FieldDefinition, AfsEngine, AfsSimulationConfig
from afs.calibrator import HomeostasisCalibrator, FieldAnalysis
from afs.analysis import detect_clusters


def create_tradeoff_field() -> FieldDefinition:
    """Field with strong positive AND negative relationships."""
    field = FieldDefinition(metadata={"problem": "Test trade-off dynamics"})
    dims = [
        ("speed", "performance"),
        ("quality", "performance"),
        ("cost", "financial"),
        ("flexibility", "design"),
        ("reliability", "design"),
    ]
    for name, cat in dims:
        dim_id = field.add_dimension(name=name, category=cat)
        field.promote_dimension(dim_id)

    matrix = np.array([
        [ 1.0, -0.8,  0.6,  0.5, -0.3],
        [-0.8,  1.0, -0.5, -0.3,  0.7],
        [ 0.6, -0.5,  1.0,  0.4, -0.6],
        [ 0.5, -0.3,  0.4,  1.0,  0.3],
        [-0.3,  0.7, -0.6,  0.3,  1.0],
    ])
    field.update_similarity_matrix(matrix)
    return field


def create_all_positive_field() -> FieldDefinition:
    """Field with only positive similarities — needs tension injection."""
    field = FieldDefinition(metadata={"problem": "All positive similarities"})
    dims = [
        ("employee_availability", "operational"),
        ("customer_demand", "operational"),
        ("labor_cost", "financial"),
        ("service_quality", "human"),
    ]
    for name, cat in dims:
        dim_id = field.add_dimension(name=name, category=cat)
        field.promote_dimension(dim_id)

    matrix = np.array([
        [1.0, 0.65, 0.72, 0.58],
        [0.65, 1.0, 0.70, 0.66],
        [0.72, 0.70, 1.0, 0.61],
        [0.58, 0.66, 0.61, 1.0],
    ])
    field.update_similarity_matrix(matrix)
    return field


def test_field_analysis_tradeoffs():
    print("test_field_analysis_tradeoffs...", end=" ")
    field = create_tradeoff_field()
    cal = HomeostasisCalibrator(field)
    analysis = cal.analyze_field()

    assert analysis.dimensions == 5
    assert analysis.total_pairs == 10
    assert analysis.negative_count > 0, "Should detect negative relationships"
    assert analysis.positive_count > 0, "Should detect positive relationships"
    assert analysis.polarity_ratio > 0.3, f"Expected strong polarity, got {analysis.polarity_ratio}"
    assert analysis.spread > 1.0, f"Expected spread > 1.0, got {analysis.spread}"
    print(f"PASS (polarity={analysis.polarity_ratio:.3f}, spread={analysis.spread:.3f})")


def test_field_analysis_all_positive():
    print("test_field_analysis_all_positive...", end=" ")
    field = create_all_positive_field()
    cal = HomeostasisCalibrator(field)
    analysis = cal.analyze_field()

    assert analysis.dimensions == 4
    assert analysis.negative_count == 0, "Should have no negative relationships"
    assert analysis.polarity_ratio == 0.0
    print(f"PASS (all positive, mean|v|={analysis.mean_abs_value:.3f})")


def test_probe_configs_count():
    print("test_probe_configs_count...", end=" ")
    field = create_tradeoff_field()
    cal = HomeostasisCalibrator(field)
    analysis = cal.analyze_field()
    configs = cal.generate_probe_configs(analysis)

    assert len(configs) == 8, f"Expected 8 configs, got {len(configs)}"
    labels = [c["label"] for c in configs]
    assert "conservative" in labels
    assert "matrix-reactive" in labels
    assert "balanced" in labels
    print("PASS (8 configs)")


def test_probe_configs_tension_aware():
    """All-positive field should get higher tradeoff amplifiers."""
    print("test_probe_configs_tension_aware...", end=" ")
    field_pos = create_all_positive_field()
    field_neg = create_tradeoff_field()

    cal_pos = HomeostasisCalibrator(field_pos)
    cal_neg = HomeostasisCalibrator(field_neg)

    analysis_pos = cal_pos.analyze_field()
    analysis_neg = cal_neg.analyze_field()

    configs_pos = cal_pos.generate_probe_configs(analysis_pos)
    configs_neg = cal_neg.generate_probe_configs(analysis_neg)

    # Conservative config: all-positive field should get higher tradeoff_amplifier
    amp_pos = configs_pos[0]["engine"]["tradeoff_amplifier"]
    amp_neg = configs_neg[0]["engine"]["tradeoff_amplifier"]
    assert amp_pos > amp_neg, f"All-positive should need more tension: {amp_pos} vs {amp_neg}"
    print(f"PASS (pos_amp={amp_pos}, neg_amp={amp_neg})")


def test_single_probe_runs():
    print("test_single_probe_runs...", end=" ")
    field = create_tradeoff_field()
    cal = HomeostasisCalibrator(field, probe_generations=10, probe_cells=16)
    analysis = cal.analyze_field()
    configs = cal.generate_probe_configs(analysis)

    score = cal.run_probe(configs[0])
    assert not score.unstable, "Conservative config should be stable"
    assert score.population > 0, "Should have surviving cells"
    assert not np.isnan(score.total), "Score should not be NaN"
    print(f"PASS (score={score.total}, pop={score.population})")


def test_full_calibration():
    """Run full calibration and verify output config is valid."""
    print("test_full_calibration...", end=" ")
    field = create_tradeoff_field()
    cal = HomeostasisCalibrator(field, probe_generations=10, probe_cells=16)
    config = cal.calibrate(verbose=False)

    assert isinstance(config, AfsSimulationConfig)
    assert config.engine.initial_cell_count == 128, "Should use target cell count"
    assert config.engine.max_population == 300, "Should use target max pop"
    assert config._calibration is not None, "Should have calibration metadata"
    # calibrate() uses adaptive bisection — labels are wide-1..4, bisect-mid, neighbor-*
    assert config._calibration["label"] is not None, "Should have a winner label"
    assert config._calibration.get("method") == "adaptive-bisection", "Should use bisection"
    assert config._calibration["score"]["total"] > 0, "Best score should be positive"
    print(f"PASS (winner={config._calibration['label']}, score={config._calibration['score']['total']})")


def test_calibrated_engine_runs():
    """Verify the calibrated config produces a working engine."""
    print("test_calibrated_engine_runs...", end=" ")
    field = create_tradeoff_field()
    cal = HomeostasisCalibrator(field, probe_generations=10, probe_cells=16, target_cells=32, target_max_pop=64)
    config = cal.calibrate(verbose=False)

    engine = AfsEngine(field, config)
    engine.initialize()
    assert len(engine.cells) == 32

    for _ in range(1000):
        engine.tick()

    positions = engine.get_cell_positions()
    assert not np.any(np.isnan(positions)), "NaN in calibrated engine"
    assert len(engine.cells) > 0, "All cells died"

    clusters = detect_clusters(positions)
    print(f"PASS (pop={len(engine.cells)}, clusters={len(clusters)})")


def test_calibration_all_positive_field():
    """All-positive field should still produce meaningful calibration."""
    print("test_calibration_all_positive_field...", end=" ")
    field = create_all_positive_field()
    cal = HomeostasisCalibrator(field, probe_generations=10, probe_cells=16)
    config = cal.calibrate(verbose=False)

    assert config._calibration["score"]["total"] > 0
    assert config._calibration["label"] is not None
    print(f"PASS (winner={config._calibration['label']}, score={config._calibration['score']['total']})")


if __name__ == "__main__":
    print("=" * 60)
    print("AFS Python Engine — Homeostasis Calibrator Tests")
    print("=" * 60)
    print()

    tests = [
        test_field_analysis_tradeoffs,
        test_field_analysis_all_positive,
        test_probe_configs_count,
        test_probe_configs_tension_aware,
        test_single_probe_runs,
        test_full_calibration,
        test_calibrated_engine_runs,
        test_calibration_all_positive_field,
    ]

    passed = 0
    failed = 0
    for test in tests:
        try:
            test()
            passed += 1
        except Exception as e:
            print(f"FAIL: {e}")
            import traceback
            traceback.print_exc()
            failed += 1

    print()
    print(f"Results: {passed} passed, {failed} failed")
