import pytest
from infer_db.connectors.factory import ConnectorFactory
from infer_db.pipeline import InferenceEngine
from infer_db.strategies.pk_strategies import ExistingConstraintStrategy, SurrogateKeyStrategy, UniquenessStrategy, DataValidationStrategy
from infer_db.strategies.fk_strategies import NamingConventionFKStrategy, ContainmentFKStrategy, ExistingConstraintFKStrategy
import infer_db.config as config

@pytest.fixture(scope="module")
def engine():
    db_config = {
        "host": config.DB_HOST,
        "dbname": config.DB_NAME,
        "user": config.DB_USER,
        "password": config.DB_PASSWORD,
        "port": config.DB_PORT
    }
    db = ConnectorFactory.create_connector("postgres", db_config)
    eng = InferenceEngine(db)
    
    eng.add_pk_strategy(ExistingConstraintStrategy())
    eng.add_pk_strategy(SurrogateKeyStrategy())
    eng.add_pk_strategy(UniquenessStrategy())
    # Assuming DataValidationStrategy is used internally or added here if needed
    
    eng.add_fk_strategy(ExistingConstraintFKStrategy())
    eng.add_fk_strategy(NamingConventionFKStrategy())
    eng.add_fk_strategy(ContainmentFKStrategy())
    
    return eng

# --- 1. NAMING TESTS ---

def test_naming_convention_valid(engine):
    """Test that standard 'user_id' -> 'users.id' is detected."""
    report = engine.run()
    relations = report['relations']
    match = next((r for r in relations if 
                  r['source_table'] == 'posts' and 
                  r['source_col'] == 'user_id' and 
                  r['target_table'] == 'users' and
                  r['strategy'] == 'NamingConvention'), None)
    assert match is not None, "Failed to detect standard naming convention"

# def test_self_reference_detection(engine):
#     """Test detection of self-referencing keys (employees.employee_id -> employees.id)."""
#     report = engine.run()
#     relations = report['relations']
#     match = next((r for r in relations if 
#                   r['source_table'] == 'employees' and 
#                   r['source_col'] == 'employee_id' and 
#                   r['target_table'] == 'employees'), None)
#     assert match is not None, "Failed to detect self-referencing Foreign Key"

# --- 2. TYPE COMPATIBILITY TESTS ---

def test_legacy_string_type_matching(engine):
    """Test that VARCHAR ID matching INTEGER PK is allowed (Type Compatibility)."""
    report = engine.run()
    relations = report['relations']
    match = next((r for r in relations if 
                  r['source_table'] == 'legacy_data' and 
                  r['source_col'] == 'user_id' and 
                  r['target_table'] == 'users'), None)
    assert match is not None, "Failed to detect cross-type FK (String -> Int)"

def test_type_mismatch_rejection(engine):
    """Test that 'user_id' (BOOLEAN) is NOT matched to 'users.id' (INTEGER)."""
    report = engine.run()
    relations = report['relations']
    match = next((r for r in relations if 
                  r['source_table'] == 'bad_naming' and 
                  r['source_col'] == 'user_id' and 
                  r['target_table'] == 'users'), None)
    assert match is None, "Engine incorrectly matched BOOLEAN 'user_id' to INTEGER 'users.id'!"

# --- 3. CONTAINMENT (VALIDATION) TESTS ---

def test_containment_rejects_orphans(engine):
    """Test that 'broken_fks.user_id' (orphans) is REJECTED by Containment."""
    report = engine.run()
    relations = report['relations']
    
    # Verify Naming found it (it looks like a key)
    naming = next((r for r in relations if 
                   r['source_table'] == 'broken_fks' and 
                   r['strategy'] == 'NamingConvention'), None)
    assert naming is not None, "Naming logic failed on broken_fks"
    
    # Verify Containment rejected it (it has bad data)
    containment = next((r for r in relations if 
                        r['source_table'] == 'broken_fks' and 
                        r['strategy'] == 'ValueContainment'), None)
    assert containment is None, "Containment passed a column with Orphan values!"

def test_containment_accepts_valid(engine):
    """Test that Containment verifies valid NATURAL keys (user_audit -> users)."""
    report = engine.run()
    relations = report['relations']
    match = next((r for r in relations if 
                  r['source_table'] == 'user_audit' and 
                  r['source_col'] == 'username' and 
                  r['target_table'] == 'users' and
                  r['strategy'] == 'ValueContainment'), None)
    assert match is not None, "Containment failed on valid natural key"