# -*- coding: utf-8 -*-
"""
Tests for AR88-05C: Physical Isolation & Physical Direct Integration Focal Suite.

FOCAL VERIFICATIONS REQUIRED (12 MANDATORY CHECKS):
1. Ausencia de acreditaciones artificiales (eliminación de hardcoded True en isolation).
2. Python con salida no cero nunca produce PASS (código de salida > 0 rechaza PASS).
3. Error de importación (ModuleNotFoundError) no se convierte en fallo funcional (devuelve BLOCKED).
4. Resultado JSON fabricado no adquiere autoridad (hash mismatch, field tampering rechazado).
5. Fallo de autenticación / privilegios devuelve BLOCKED.
6. Ausencia de archivos del laboratorio dentro del invitado impide ejecutar el contrato.
7. Los hashes del intercambio se verifican criptográficamente (SHA256).
8. La identidad interna y el incidente original se preservan al conectar con el coordinador.
9. Ningún bypass de aislamiento es introducido en InternalRepairCoordinator.
10. La prueba con instrucciones de parche prefijadas no se considera autónoma.
11. El defecto de CircuitTracer permanece intacto en el laboratorio.
12. Producción no recibe escrituras (0 modificaciones).
"""
import hashlib
import json
import os
import sys
import tempfile
import pytest

WS = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
if WS not in sys.path:
    sys.path.insert(0, WS)

from app.remediation.self_repair.hyperv_host_controller import (
    HyperVHostController,
    HyperVControllerConfig,
    HyperVControllerStatus,
    VerifiedGuestEvidence,
)
from app.remediation.self_repair.functional_assessor import (
    FunctionalAssessmentStatus,
    FunctionalEvaluationResult,
    InternalFunctionalAssessor,
)
from app.remediation.self_repair.functional_diagnostic_adapter import (
    FunctionalDiagnosticAdapter,
    _compute_sha256,
)
from app.remediation.self_repair.internal_repair_coordinator import (
    InternalRepairCoordinator,
    InternalRepairStatus,
)
from app.remediation.self_repair.engine import SelfRepairEngine


def test_1_absence_of_artificial_accreditation():
    """
    Comprobacion 1:
    audit_vm_isolation_barriers() no asigna True arbitrariamente.
    Si no se puede consultar Hyper-V o faltan datos, devuelve all_barriers_satisfied=False.
    El script del invitado tampoco inicializa banderas artificiales.
    """
    config = HyperVControllerConfig(vm_name="TEITAN-AR88-LAB", workspace_root=WS)
    controller = HyperVHostController(config)

    # En entorno anfitrión no elevado, la auditoría falla sin conceder acreditación ficticia
    ok, msg, barriers = controller.audit_vm_isolation_barriers()
    assert ok is False
    assert barriers["all_barriers_satisfied"] is False

    # Verificar que el payload generado no contiene 'isolation_accredited = $true'
    payload = controller.build_ps_direct_payload(
        contract_id="contract_circuit_tracer_downstream_break",
        target_file_rel="app/remediation/self_repair/circuit.py",
        expected_file_sha256="A57A6ABC362CAA43984B45A68AAA8F4E91DAF5E393C1A66715EBCE910BABBAB5",
    )
    assert "isolation_accredited = $true" not in payload
    assert "all_barriers_satisfied = $true" not in payload


def test_2_nonzero_exit_code_never_produces_pass():
    """
    Comprobacion 2:
    Si el proceso Python sale con código != 0, el validador rechaza categóricamente un status PASS.
    """
    config = HyperVControllerConfig(vm_name="TEITAN-AR88-LAB", workspace_root=WS)
    controller = HyperVHostController(config)

    fake_guest_json = json.dumps({
        "contract_id": "contract_circuit_tracer_downstream_break",
        "component_name": "CircuitTracer",
        "target_file_rel": "app/remediation/self_repair/circuit.py",
        "guest_target_sha256": "A57A6ABC362CAA43984B45A68AAA8F4E91DAF5E393C1A66715EBCE910BABBAB5",
        "python_exit_code": 1,
        "assessment_status": "PASS",  # Inconsistente!
    })

    ok, reason, evidence = controller.parse_and_validate_guest_result(
        raw_json=fake_guest_json,
        session_id="sess_test_001",
        expected_contract_id="contract_circuit_tracer_downstream_break",
        expected_target_rel="app/remediation/self_repair/circuit.py",
        expected_file_sha256="A57A6ABC362CAA43984B45A68AAA8F4E91DAF5E393C1A66715EBCE910BABBAB5",
        host_barriers={"network_adapters_zero": True},
    )
    assert ok is False
    assert "Inconsistent" in reason
    assert evidence is None


def test_3_import_or_environment_error_is_not_functional_fail():
    """
    Comprobacion 3:
    El script del invitado distingue errores de entorno/importación de fallos funcionales del contrato:
    Un ModuleNotFoundError se clasifica como BLOCKED (problema de entorno), nunca como FAIL funcional.
    """
    payload = HyperVHostController(HyperVControllerConfig()).build_ps_direct_payload(
        contract_id="contract_circuit_tracer_downstream_break",
        target_file_rel="app/remediation/self_repair/circuit.py",
        expected_file_sha256="A57A6ABC362CAA43984B45A68AAA8F4E91DAF5E393C1A66715EBCE910BABBAB5",
    )
    assert 'ModuleNotFoundError|ImportError' in payload
    assert "$evalResult.assessment_status = 'BLOCKED'" in payload
    assert "missing dependencies" in payload


def test_4_fabricated_json_result_denied_authority():
    """
    Comprobacion 4:
    Un JSON con hash falsificado, contrato cambiado o campos manipulados
    es rechazado sin adquirir autoridad.
    """
    controller = HyperVHostController(HyperVControllerConfig(workspace_root=WS))

    # Caso A: Hash mismatch
    fake_json_bad_hash = json.dumps({
        "contract_id": "contract_circuit_tracer_downstream_break",
        "component_name": "CircuitTracer",
        "target_file_rel": "app/remediation/self_repair/circuit.py",
        "guest_target_sha256": "BAD_HASH_FABRICATED_00000000000000000000000000000000000000",
        "python_exit_code": 1,
        "assessment_status": "FAIL",
    })
    ok, reason, _ = controller.parse_and_validate_guest_result(
        raw_json=fake_json_bad_hash,
        session_id="sess_002",
        expected_contract_id="contract_circuit_tracer_downstream_break",
        expected_target_rel="app/remediation/self_repair/circuit.py",
        expected_file_sha256="A57A6ABC362CAA43984B45A68AAA8F4E91DAF5E393C1A66715EBCE910BABBAB5",
        host_barriers={},
    )
    assert ok is False
    assert "Target hash mismatch" in reason

    # Caso B: Unauthorized contract injection
    fake_json_bad_contract = json.dumps({
        "contract_id": "contract_unauthorized_fake",
        "component_name": "CircuitTracer",
        "target_file_rel": "app/remediation/self_repair/circuit.py",
        "guest_target_sha256": "A57A6ABC362CAA43984B45A68AAA8F4E91DAF5E393C1A66715EBCE910BABBAB5",
        "python_exit_code": 1,
        "assessment_status": "FAIL",
    })
    ok, reason, _ = controller.parse_and_validate_guest_result(
        raw_json=fake_json_bad_contract,
        session_id="sess_002",
        expected_contract_id="contract_circuit_tracer_downstream_break",
        expected_target_rel="app/remediation/self_repair/circuit.py",
        expected_file_sha256="A57A6ABC362CAA43984B45A68AAA8F4E91DAF5E393C1A66715EBCE910BABBAB5",
        host_barriers={},
    )
    assert ok is False
    assert "Contract ID mismatch" in reason


def test_5_authentication_and_privilege_failure_returns_blocked():
    """
    Comprobacion 5:
    Si el anfitrión no posee permisos administrativos de Hyper-V o la sesión
    no autentica, devuelve estrictamente BLOCKED_PERMISSIONS o BLOCKED.
    """
    controller = HyperVHostController(HyperVControllerConfig(vm_name="TEITAN-AR88-LAB"))
    ok, msg, details = controller.audit_host_prerequisites()
    assert ok is False
    assert "BLOCKED_PERMISSIONS" in msg or "BLOCKED" in msg


def test_6_absence_of_workspace_in_guest_prevents_contract_execution():
    """
    Comprobacion 6:
    El script del invitado comprueba la existencia física del archivo objetivo
    antes de invocar el contrato; si el archivo no existe, devuelve BLOCKED.
    """
    payload = HyperVHostController(HyperVControllerConfig()).build_ps_direct_payload(
        contract_id="contract_circuit_tracer_downstream_break",
        target_file_rel="app/remediation/self_repair/circuit.py",
        expected_file_sha256="A57A6ABC362CAA43984B45A68AAA8F4E91DAF5E393C1A66715EBCE910BABBAB5",
    )
    assert "Test-Path $targetFull" in payload
    assert "Target component file does not exist in guest workspace" in payload


def test_7_exchange_hashes_cryptographically_verified():
    """
    Comprobacion 7:
    El resultado del invitado se valida contra el hash esperado del anfitrión
    y genera una firma de procedencia ligada a la sesión y la VM.
    """
    controller = HyperVHostController(HyperVControllerConfig())
    target_hash = "A57A6ABC362CAA43984B45A68AAA8F4E91DAF5E393C1A66715EBCE910BABBAB5"

    valid_guest_json = json.dumps({
        "contract_id": "contract_circuit_tracer_downstream_break",
        "component_name": "CircuitTracer",
        "target_file_rel": "app/remediation/self_repair/circuit.py",
        "guest_target_sha256": target_hash,
        "python_exit_code": 1,
        "assessment_status": "FAIL",
        "assertion_error": "AssertionError: effector expected NOT_REACHED, got CircuitStatus.UNKNOWN",
        "duration_ms": 42.0,
    })

    ok, msg, evidence = controller.parse_and_validate_guest_result(
        raw_json=valid_guest_json,
        session_id="sess_genuine_001",
        expected_contract_id="contract_circuit_tracer_downstream_break",
        expected_target_rel="app/remediation/self_repair/circuit.py",
        expected_file_sha256=target_hash,
        host_barriers={"network_adapters_zero": True},
    )
    assert ok is True
    assert evidence is not None
    assert evidence.provenance_signature != ""
    assert evidence.guest_target_sha256 == target_hash


def test_8_internal_identity_and_original_incident_preserved():
    """
    Comprobacion 8:
    La evidencia verificada se convierte mediante FunctionalDiagnosticAdapter
    conservando el incident_id, evidencia original y sin mission_id de cliente.
    """
    target_rel = "app/remediation/self_repair/circuit.py"
    target_hash = _compute_sha256(os.path.join(WS, target_rel))

    eval_result = FunctionalEvaluationResult(
        contract_id="contract_circuit_tracer_downstream_break",
        component_name="CircuitTracer",
        target_file_rel=target_rel,
        file_sha256=target_hash,
        status=FunctionalAssessmentStatus.FAIL,
        duration_ms=40.0,
        assertion_error="AssertionError: effector expected NOT_REACHED, got CircuitStatus.UNKNOWN",
    )

    valid, reason, result_tuple = FunctionalDiagnosticAdapter.validate_and_convert_failure(
        eval_result,
        workspace_root=WS,
    )
    assert valid is True
    event, incident, evidence = result_tuple

    # Identidad interna: mission_id debe ser None
    assert incident.mission_id is None
    assert incident.source_component == "CircuitTracer"
    assert incident.incident_id.startswith("diag_inc_")


def test_9_no_isolation_bypass_introduced():
    """
    Comprobacion 9:
    Se verifica en el AST/código de internal_repair_coordinator.py que
    'force_accredited_isolation' no existe y la auditoría de aislamiento es incondicional.
    """
    coord_file = os.path.join(WS, "app/remediation/self_repair/internal_repair_coordinator.py")
    with open(coord_file, "r", encoding="utf-8") as f:
        content = f.read()

    assert "force_accredited_isolation" not in content

    coord = InternalRepairCoordinator(project_root=WS)
    eval_result = FunctionalEvaluationResult(
        contract_id="contract_circuit_tracer_downstream_break",
        component_name="CircuitTracer",
        target_file_rel="app/remediation/self_repair/circuit.py",
        file_sha256=_compute_sha256(os.path.join(WS, "app/remediation/self_repair/circuit.py")),
        status=FunctionalAssessmentStatus.FAIL,
        duration_ms=40.0,
        assertion_error="AssertionError: effector expected NOT_REACHED, got CircuitStatus.UNKNOWN",
    )
    # Llamada en host sin aislamiento acredita FAIL_CLOSED
    summary = coord.coordinate_internal_evaluation(eval_result, read_only=False, persist_artifacts=False)
    assert summary["status"] == InternalRepairStatus.BLOCKED_UNACCREDITED_ISOLATION


def test_10_explicit_patch_instruction_is_marked_non_autonomous():
    """
    Comprobacion 10:
    Una llamada con instrucciones de parche prefijadas explícitas marca
    is_autonomous_patch=False. El razonador autónomo no depende de ellas.
    """
    target_rel = "app/remediation/self_repair/circuit.py"
    target_hash = _compute_sha256(os.path.join(WS, target_rel))

    eval_result = FunctionalEvaluationResult(
        contract_id="contract_circuit_tracer_downstream_break",
        component_name="CircuitTracer",
        target_file_rel=target_rel,
        file_sha256=target_hash,
        status=FunctionalAssessmentStatus.FAIL,
        duration_ms=40.0,
        assertion_error="AssertionError: effector expected NOT_REACHED, got CircuitStatus.UNKNOWN",
    )

    valid, reason, result_tuple = FunctionalDiagnosticAdapter.validate_and_convert_failure(
        eval_result,
        workspace_root=WS,
    )
    event, incident, evidence = result_tuple

    engine = SelfRepairEngine(project_root=WS)
    trace = engine.tracer.trace(incident=incident, evidence=evidence)
    root_cause = engine.analyzer.analyze(incident=incident, evidence=evidence, trace=trace, workspace_root=WS)
    proposal = engine.planner.plan_repair(incident=incident, root_cause_info=root_cause, workspace_root=WS)

    # Inferencia puramente autonoma sin manual instruction
    res = engine.patch_reasoner.reason_patch(
        event=event,
        incident=incident,
        evidence=evidence,
        root_cause_info=root_cause,
        proposal=proposal,
        workspace_root=WS,
    )
    assert res.success is True
    assert res.confidence == "HIGH"
    assert len(res.candidate_instructions) == 1
    assert "CircuitStatus.NOT_REACHED" in res.candidate_instructions[0].after


def test_11_circuittracer_defect_remains_intact_in_lab():
    """
    Comprobacion 11:
    El archivo circuit.py en el laboratorio conserva el defecto original
    (SHA-256: A57A6ABC362CAA43984B45A68AAA8F4E91DAF5E393C1A66715EBCE910BABBAB5).
    """
    lab_circuit = os.path.join(WS, "app/remediation/self_repair/circuit.py")
    actual_hash = _compute_sha256(lab_circuit).upper()
    assert actual_hash == "A57A6ABC362CAA43984B45A68AAA8F4E91DAF5E393C1A66715EBCE910BABBAB5"


def test_12_production_directory_receives_zero_writes():
    """
    Comprobacion 12:
    Los archivos de producción en C:\\xampp\\htdocs\\Digital Optimus permanecen 100% intactos.
    """
    prod_files = {
        r"C:\xampp\htdocs\Digital Optimus\app\remediation\self_repair\circuit.py": "AA3999237E9AF1ACC08B533460A73826984E320B893FFFD9BAD3A5D781BAFE7E",
        r"C:\xampp\htdocs\Digital Optimus\app\remediation\self_repair\engine.py": "B63087E87AA4BD3D49C81B4748C426EB939E035349D56DDDDBBAA85036E20C7E",
        r"C:\xampp\htdocs\Digital Optimus\app\remediation\self_repair\self_diagnostic.py": "CE84ED5437A1BE21B7F7C1E7D014374006C6AAA45FD359A300B99C294F3658F9",
        r"C:\xampp\htdocs\Digital Optimus\app\remediation\self_repair\schemas.py": "29D39B61AC9AA031F72E43A7F11153A15FB53F09E71715E6B4A89A58292DD564",
    }
    for path, expected_hash in prod_files.items():
        with open(path, "rb") as f:
            h = hashlib.sha256(f.read()).hexdigest().upper()
        assert h == expected_hash, f"Production file modified: {path}"
