"""
Argus Memory System - Integration Tests
Validates structural integrity and cross-references
"""
import yaml
import os
import sys
from pathlib import Path
from datetime import datetime

# Resolve paths dynamically relative to this script's location
# Script lives in Argus/tests/, so parent is Argus/ (project root)
BASE_DIR = Path(__file__).resolve().parent.parent
MEMORY_DIR = BASE_DIR / "memory"

class _TestResult:
    def __init__(self, name):
        self.name = name
        self.passed = 0
        self.failed = 0
        self.warnings = 0
        self.details = []
    
    def ok(self, msg):
        self.passed += 1
        self.details.append(f"  [PASS] {msg}")
    
    def fail(self, msg):
        self.failed += 1
        self.details.append(f"  [FAIL] {msg}")
    
    def warn(self, msg):
        self.warnings += 1
        self.details.append(f"  [WARN] {msg}")
    
    def __str__(self):
        status = "PASS" if self.failed == 0 else "FAIL"
        return f"\n{self.name}: {status} ({self.passed} passed, {self.failed} failed, {self.warnings} warnings)"

def load_yaml(filepath):
    with open(filepath, 'r', encoding='utf-8') as f:
        return yaml.safe_load(f)

def test_index_counters():
    """Test that _index.yaml counters match actual content"""
    result = _TestResult("Index Counters Consistency")
    
    index = load_yaml(MEMORY_DIR / "_index.yaml")
    
    # Check heuristics count - dynamically sum all sections that contain lists
    heuristics = load_yaml(MEMORY_DIR / "semantic" / "testing_heuristics.yaml")
    HEURISTIC_META_KEYS = {'version', 'last_updated', 'metadata'}
    actual_heuristics = sum(
        len(v) for k, v in heuristics.items()
        if k not in HEURISTIC_META_KEYS and isinstance(v, list)
    )
    
    expected_heuristics = index['semantic']['testing_heuristics']['count']
    if actual_heuristics == expected_heuristics:
        result.ok(f"Heuristics count matches: {actual_heuristics}")
    else:
        result.fail(f"Heuristics count mismatch: index says {expected_heuristics}, actual is {actual_heuristics}")
    
    # Check patterns count
    patterns = load_yaml(MEMORY_DIR / "semantic" / "defect_patterns.yaml")
    actual_patterns = len(patterns.get('patterns', []))
    expected_patterns = index['semantic']['defect_patterns']['count']
    if actual_patterns == expected_patterns:
        result.ok(f"Patterns count matches: {actual_patterns}")
    else:
        result.fail(f"Patterns count mismatch: index says {expected_patterns}, actual is {actual_patterns}")
    
    # Check sequence counters
    if index['sequences']['heuristic'] == expected_heuristics:
        result.ok(f"Sequence counter heuristic matches: {index['sequences']['heuristic']}")
    else:
        result.warn(f"Sequence counter heuristic may be outdated: {index['sequences']['heuristic']} vs {expected_heuristics}")
    
    assert result.failed == 0, str(result)

def test_project_exists_in_index():
    """Test that registered projects have corresponding files"""
    result = _TestResult("Project Files Existence")
    
    index = load_yaml(MEMORY_DIR / "_index.yaml")
    projects_dir = MEMORY_DIR / "projects"
    
    # Check if projects dir has yaml files (excluding templates)
    project_files = [f for f in projects_dir.glob("*.yaml") if not f.name.startswith("_")]
    
    registered = index['projects'].get('registered', [])
    expected_count = index['projects']['count']
    
    if len(project_files) == expected_count:
        result.ok(f"Project count matches: {len(project_files)}")
    else:
        result.warn(f"Project count in index ({expected_count}) differs from files ({len(project_files)})")
    
    # List found projects
    for pf in project_files:
        result.ok(f"Found project file: {pf.name}")
    
    assert result.failed == 0, str(result)

def test_episodic_files():
    """Test that episodic files follow naming conventions"""
    result = _TestResult("Episodic File Naming")
    
    episodic_dir = MEMORY_DIR / "episodic"
    
    for subdir in ['analyses', 'decisions', 'incidents', 'executions']:
        dir_path = episodic_dir / subdir
        files = [f for f in dir_path.glob("*.yaml") if not f.name.startswith("_")]
        
        for f in files:
            # Check naming pattern: YYYY-MM-DD_HH-MM_type_id.yaml
            parts = f.stem.split("_")
            if len(parts) >= 4:
                date_part = parts[0]
                try:
                    datetime.strptime(date_part, "%Y-%m-%d")
                    result.ok(f"{subdir}/{f.name}: Valid naming")
                except ValueError:
                    result.fail(f"{subdir}/{f.name}: Invalid date format")
            else:
                result.warn(f"{subdir}/{f.name}: Non-standard naming")
    
    assert result.failed == 0, str(result)

def test_template_completeness():
    """Test that templates have all required fields"""
    result = _TestResult("Template Completeness")
    
    templates = {
        "analysis": ("analyses", ["id", "timestamp", "project", "trigger", "scope", "findings", "verdict"]),
        "decision": ("decisions", ["id", "timestamp", "context", "options", "decision", "rationale"]),
        "incident": ("incidents", ["id", "detected_at", "severity", "project", "description", "root_cause"]),
        "execution": ("executions", ["id", "started_at", "project", "suites", "summary"])
    }
    
    for template_type, (folder, required_fields) in templates.items():
        template_path = MEMORY_DIR / "episodic" / folder / f"_template_{template_type}.yaml"
        
        if template_path.exists():
            template = load_yaml(template_path)
            missing = [f for f in required_fields if f not in template]
            
            if not missing:
                result.ok(f"Template {template_type}: All required fields present")
            else:
                result.fail(f"Template {template_type}: Missing fields: {missing}")
        else:
            result.fail(f"Template {template_type}: File not found")
    
    assert result.failed == 0, str(result)

def test_istqb_integration():
    """Test that ISTQB knowledge is properly integrated"""
    result = _TestResult("ISTQB Integration")
    
    istqb = load_yaml(MEMORY_DIR / "semantic" / "istqb_knowledge.yaml")
    
    # Check 7 principles
    principles = istqb.get('principles', [])
    if len(principles) == 7:
        result.ok("All 7 ISTQB principles present")
    else:
        result.fail(f"Expected 7 principles, found {len(principles)}")
    
    # Check test levels
    test_levels = istqb.get('test_levels', {})
    expected_levels = ['unit_testing', 'integration_testing', 'system_testing', 'acceptance_testing']
    for level in expected_levels:
        if level in test_levels:
            result.ok(f"Test level '{level}' present")
        else:
            result.fail(f"Test level '{level}' missing")
    
    # Check black box techniques
    bb_techniques = istqb.get('black_box_techniques', {})
    expected_bb = ['equivalence_partitioning', 'boundary_value_analysis', 'decision_table_testing', 'state_transition_testing']
    for tech in expected_bb:
        if tech in bb_techniques:
            result.ok(f"Black-box technique '{tech}' present")
        else:
            result.fail(f"Black-box technique '{tech}' missing")
    
    assert result.failed == 0, str(result)

def test_cross_references():
    """Test that cross-references between files are valid"""
    result = _TestResult("Cross-References Validity")
    
    # Check heuristics that reference patterns
    heuristics = load_yaml(MEMORY_DIR / "semantic" / "testing_heuristics.yaml")
    patterns = load_yaml(MEMORY_DIR / "semantic" / "defect_patterns.yaml")
    
    pattern_ids = [p['id'] for p in patterns.get('patterns', [])]
    
    # Check if HEU-ISTQB-007 references defect_patterns correctly
    for section in ['istqb_techniques']:
        for h in heuristics.get(section, []):
            if h.get('id') == 'HEU-ISTQB-007':
                if 'link' in h and h['link'] == 'defect_patterns.yaml':
                    result.ok("HEU-ISTQB-007 correctly references defect_patterns.yaml")
                else:
                    result.warn("HEU-ISTQB-007 link to defect_patterns may be missing")
    
    # Check _index references to semantic files
    index = load_yaml(MEMORY_DIR / "_index.yaml")
    for key in ['defect_patterns', 'testing_heuristics', 'technology_knowledge']:
        if key in index.get('semantic', {}):
            path = index['semantic'][key].get('path', '')
            full_path = BASE_DIR / path
            if full_path.exists():
                result.ok(f"Index path '{path}' exists")
            else:
                result.fail(f"Index path '{path}' does not exist")
    
    assert result.failed == 0, str(result)

def run_all_tests():
    """Run all tests and report results"""
    print("=" * 70)
    print("  ARGUS MEMORY SYSTEM - INTEGRATION TESTS")
    print("=" * 70)
    print(f"  Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print("=" * 70)
    
    tests = [
        test_index_counters,
        test_project_exists_in_index,
        test_episodic_files,
        test_template_completeness,
        test_istqb_integration,
        test_cross_references,
    ]
    
    all_results = []
    total_passed = 0
    total_failed = 0
    total_warnings = 0
    
    for test_func in tests:
        try:
            test_func()
            total_passed += 1
            print(f"\n  {test_func.__name__}: PASS")
        except AssertionError as e:
            total_failed += 1
            print(f"\n  {test_func.__name__}: FAIL — {e}")
        except Exception as e:
            print(f"\n  {test_func.__name__}: ERROR")
            print(f"    Exception: {e}")
            total_failed += 1
    
    print("\n" + "=" * 70)
    print("  SUMMARY")
    print("=" * 70)
    print(f"  Total tests:    {len(tests)}")
    print(f"  Total passed:   {total_passed}")
    print(f"  Total failed:   {total_failed}")
    print(f"  Total warnings: {total_warnings}")
    
    status = "PASSED" if total_failed == 0 else "FAILED"
    print(f"\n  Overall status: {status}")
    print("=" * 70)
    
    return total_failed == 0

if __name__ == "__main__":
    success = run_all_tests()
    sys.exit(0 if success else 1)