"""Tests for the Product Profile convergence layer.

Covers the 10 acceptance criteria of the CanonicalProductRegulatoryProfile spec:
lossless ProductWizard mapping, company-profile prefill, AI stays delegated,
markets no longer hardcoded, and the new Navigator fields (role/radio/usage-data/
lifecycle/BOM) plus one-semantic-profile across reasoning + gap.
"""

from __future__ import annotations

from compliance.profile import (
    CanonicalLifecyclePhase,
    CanonicalProductRegulatoryProfile,
    CanonicalProductType,
    ComponentKind,
    EconomicOperatorRole,
    ProductComponent,
    from_company_profile,
    from_product_wizard,
    to_gap_profile,
    to_reasoning_profile,
)
from compliance.reasoning import discover_scope
from compliance.reasoning.enums import ManufacturerRole, ProductLifecyclePhase

# A realistic ProductWizard payload — exactly the gap.ProductProfile JSON shape.
WIZARD = {
    "name": "Industriespülmaschine",
    "description": "vernetzte Spülmaschine",
    "product_type": "machinery",
    "technologies": ["cloud", "ota_updates", "sensor", "actuator"],
    "data_processing": ["telemetry"],
    "markets": ["EU"],
    "connected_to_internet": True,
    "has_software_updates": True,
    "uses_ai": False,
    "processes_personal_data": False,
    "is_critical_infra_supplier": False,
    "existing_certifications": ["CE"],
    "applied_norms": ["ISO12100"],
    "has_risk_assessment": True,
    "has_technical_file": True,
    "has_operating_manual": True,
    "has_sbom": False,
    "has_vuln_management": False,
    "has_update_mechanism": True,
    "has_incident_response": False,
    "has_supply_chain_mgmt": False,
    "ce_marking_since": "",
    "product_age": "5",
}

COMPANY = {
    "company_name": "ACME Maschinen GmbH",
    "industry": "Maschinenbau",
    "business_model": "B2B",
    "company_size": "medium",
    "target_markets": ["DE", "EU"],
    "primary_jurisdiction": "DE",
    "headquarters_country": "DE",
    "uses_ai": False,
    "is_data_controller": True,
    "machine_builder": {
        "productDescription": "Industriespülmaschine",
        "productTypes": ["special_machine"],
        "containsSoftware": True,
        "containsFirmware": True,
        "containsAI": False,
        "hasSafetyFunction": True,
        "safetyFunctionDescription": "Türverriegelung",
        "isNetworked": True,
        "hasRemoteAccess": True,
        "hasOTAUpdates": True,
        "hasRiskAssessment": True,
        "criticalSectorClients": False,
    },
}


# 1. ProductWizard data maps losslessly into the canonical and back to gap shape.
def test_product_wizard_lossless_roundtrip():
    canonical = from_product_wizard(WIZARD)
    assert to_gap_profile(canonical) == WIZARD


# 2. company-profile can prefill the canonical profile.
def test_company_profile_prefill():
    c = from_company_profile(COMPANY)
    assert c.sector_industry == "Maschinenbau"
    assert c.b2b_or_b2c == "B2B"
    assert c.company_size == "medium"
    assert "DE" in c.markets and "EU" in c.markets
    assert c.has_safety_function is True
    assert c.has_remote_access is True
    assert c.has_embedded_software is True
    assert c.is_machine is True
    assert c.description == "Industriespülmaschine"


# 3. AI-Act/ucca stays delegated — only uses_ai is forwarded, no risk classification.
def test_ai_classification_stays_delegated():
    c = CanonicalProductRegulatoryProfile(name="X", uses_ai=True)
    rp = to_reasoning_profile(c)
    assert rp.has_ai_functionality is True
    assert not hasattr(rp, "ai_risk_category")  # no AI classification produced here


# 4. markets are a real list, never hardcoded ['EU'].
def test_markets_not_hardcoded_eu():
    assert CanonicalProductRegulatoryProfile(name="X").markets == []
    c = from_product_wizard({**WIZARD, "markets": ["US", "JP", "CA"]})
    assert c.markets == ["US", "JP", "CA"]
    assert to_gap_profile(c)["markets"] == ["US", "JP", "CA"]
    assert to_reasoning_profile(c).eu_market is False  # non-EU markets -> not EU


# 5. economic-operator role exists and maps to the reasoning role.
def test_economic_operator_role_exists():
    c = CanonicalProductRegulatoryProfile(name="X", economic_operator_role=EconomicOperatorRole.IMPORTER)
    assert to_reasoning_profile(c).manufacturer_role == ManufacturerRole.IMPORTER


# 6. radio_module exists (direct + inferred from a BOM component).
def test_radio_module_exists():
    assert to_reasoning_profile(CanonicalProductRegulatoryProfile(name="X", has_radio_module=True)).has_radio_module is True
    c = CanonicalProductRegulatoryProfile(name="X", components=[ProductComponent(name="WLAN", kind=ComponentKind.RADIO_MODULE)])
    assert to_reasoning_profile(c).has_radio_module is True


# 7. generates_usage_data exists (direct + inferred from telemetry).
def test_generates_usage_data_exists():
    c = CanonicalProductRegulatoryProfile(name="X", generates_usage_data=True)
    assert to_reasoning_profile(c).generates_usage_data is True
    inferred = from_product_wizard(WIZARD)  # data_processing has telemetry
    assert to_reasoning_profile(inferred).generates_usage_data is True


# 8. lifecycle_phase exists and maps.
def test_lifecycle_phase_exists():
    c = CanonicalProductRegulatoryProfile(name="X", lifecycle_phase=CanonicalLifecyclePhase.MAINTENANCE)
    assert to_reasoning_profile(c).lifecycle_phase == ProductLifecyclePhase.MAINTENANCE


# 9. BOM components are structured.
def test_bom_components_structured():
    c = CanonicalProductRegulatoryProfile(
        name="Spülmaschine",
        components=[
            ProductComponent(name="Umwälzpumpe", kind=ComponentKind.PUMP),
            ProductComponent(name="Heizung", kind=ComponentKind.HEATING),
            ProductComponent(name="SPS", kind=ComponentKind.PLC),
            ProductComponent(name="Abwasserablauf", kind=ComponentKind.WASTEWATER_OUTLET),
        ],
    )
    kinds = {comp.kind for comp in c.components}
    assert ComponentKind.PLC in kinds and ComponentKind.WASTEWATER_OUTLET in kinds


# 10. reasoning engine + gap engine run off ONE semantic profile.
def test_one_semantic_profile_reasoning_and_gap():
    canonical = CanonicalProductRegulatoryProfile(
        name="Industriespülmaschine",
        product_type=CanonicalProductType.MACHINERY,
        economic_operator_role=EconomicOperatorRole.MANUFACTURER,
        markets=["EU", "DE"],
        is_machine=True,
        has_safety_function=True,
        has_remote_access=True,
        has_software_updates=True,
        has_embedded_software=True,
        technologies=["cloud", "ota_updates"],
    )
    gap = to_gap_profile(canonical)
    rp = to_reasoning_profile(canonical)

    # same facts, two projections
    assert gap["markets"] == ["EU", "DE"]
    assert rp.eu_market is True
    assert rp.has_remote_access is True
    assert rp.has_cloud_connection is True
    assert rp.is_machine is True
    assert rp.manufacturer_role == ManufacturerRole.MANUFACTURER

    # the projected reasoning profile actually drives the reasoning engine
    scope = discover_scope(rp)
    applicable = {r.regulation_id for r in scope.applicable_regulations}
    assert "CRA" in applicable
    assert "MaschinenVO" in applicable
