# -*- coding: utf-8 -*-
"""
TEITAN SELF-REPAIR — INTERNAL AUTONOMOUS SELF-REPAIR COORDINATOR (AR88-04)
Orchestrates the internal autonomous recovery loop for TEITAN's own components.

STRICT INVARIANTS:
1. EXCLUSIVE PROVENANCE & DEDICATED IDENTITY:
   Only accepts incidents verified by FunctionalDiagnosticAdapter as authentic internal
   contract failures with valid registered contract specifications. Rejects unaccredited,
   synthetic, fabricated, or duplicate evaluation results.
2. DEDICATED INTERNAL RECOVERY PIPELINE:
   Does NOT call auto_remediate_mission(). Does NOT fabricate synthetic client mission IDs.
   Operates strictly on canonical IncidentRecord (with mission_id=None) and EvidenceBundle.
3. FAIL-CLOSED DEFENSIVE GATE:
   If status is BLOCKED, UNPROVEN, or PASS, no patch planning or execution is performed.
   If OS isolation barriers are unsatisfied (all_barriers_satisfied=False), dynamic execution
   remains unconditionally blocked.
4. REUSE OF EXISTING ENGINE COMPONENTS:
   Leverages SelfRepairEngine (CircuitTracer, RootCauseAnalyzer, RepairPlanner,
   SandboxBuilder, PatchExecutor, CandidateVerifier, EvidenceComparator, CandidateJudge).
5. DEDUPLICATION & CONTINUOUS OBSERVATION:
   Maintains a cryptographically grounded history of processed incident signatures to
   prevent infinite loops, replay attacks, or re-executing identical failures.
6. SANDBOX CONFINEMENT & ZERO PRODUCTION WRITE:
   All candidate patches, baseline snapshots, compile checks, and focused/regression tests
   are confined to the sandbox directory. Production writes and auto-promotion are strictly False.
"""
from __future__ import annotations

import hashlib
import logging
import os
from typing import Any, Dict, List, Optional, Set, Tuple

from app.remediation.self_repair.engine import SelfRepairEngine
from app.remediation.self_repair.functional_assessor import (
    FunctionalAssessmentStatus,
    FunctionalEvaluationResult,
    InternalFunctionalAssessor,
    _AUTHORIZED_INTERNAL_CONTRACTS,
)
from app.remediation.self_repair.functional_diagnostic_adapter import (
    FunctionalDiagnosticAdapter,
    _compute_sha256,
)
from app.remediation.self_repair.schemas import (
    CandidateVerdict,
    CircuitTrace,
    ComparisonResult,
    EvidenceBundle,
    IncidentRecord,
    PatchInstruction,
    PatchOperationType,
    PatchPlan,
    PatchResult,
    PromotionProposal,
    RepairProposal,
    RootCauseClassification,
    SandboxManifest,
    VerificationResult,
    _new_id,
    _utc_iso,
)

logger = logging.getLogger("teitan.self_repair.internal_coordinator")


class InternalRepairCoordinator:
    """
    Dedicated coordinator for TEITAN internal component self-repair.
    """

    def __init__(
        self,
        project_root: str,
        engine: Optional[SelfRepairEngine] = None,
        base_reports_dir: str = "reports/self_repair",
    ):
        self.project_root = os.path.abspath(project_root)
        self.base_reports_dir = base_reports_dir
        self.engine = engine or SelfRepairEngine(
            base_reports_dir=self.base_reports_dir,
            project_root=self.project_root,
        )
        self._processed_signatures: Set[str] = set()

    def compute_evaluation_signature(self, eval_result: FunctionalEvaluationResult) -> str:
        """Computes an immutable content signature to detect duplicate or replayed results."""
        raw = f"{eval_result.contract_id}:{eval_result.component_name}:{eval_result.target_file_rel}:{eval_result.file_sha256}:{eval_result.status}:{eval_result.assertion_error}"
        return hashlib.sha256(raw.encode("utf-8")).hexdigest()

    def coordinate_internal_evaluation(
        self,
        eval_result: FunctionalEvaluationResult,
        read_only: bool = False,
        explicit_patch_instruction: Optional[Dict[str, Any]] = None,
        focused_test_runner: Optional[Any] = None,
        regression_test_runner: Optional[Any] = None,
        persist_artifacts: bool = True,
    ) -> Dict[str, Any]:
        """
        Coordinates the complete internal recovery flow:
        Validation -> De-duplication -> Incident Delivery -> Trace -> Root Cause ->
        Proposal -> Sandbox Build -> Candidate Patch -> Verify -> Compare -> Judge.
        """
        result: Dict[str, Any] = {
            "coordinator": "InternalRepairCoordinator",
            "contract_id": eval_result.contract_id,
            "component_name": eval_result.component_name,
            "status": "INITIATED",
            "internal_incident_admitted": False,
            "trace_complete": False,
            "root_cause_proven": False,
            "sandbox_repair_executed": False,
            "candidate_verdict": None,
            "promotion_proposed": False,
            "production_write_authority": False,
            "auto_promotion": False,
            "error": None,
        }

        # 1. State Filter (FAIL ONLY: Reject BLOCKED, PASS, UNPROVEN)
        raw_status = str(eval_result.status or "").strip().upper()
        if raw_status != FunctionalAssessmentStatus.FAIL:
            result.update({
                "status": f"REJECTED_{raw_status}",
                "error": f"Evaluation status '{raw_status}' cannot trigger internal repair.",
            })
            return result

        # 2. De-duplication Guard
        sig = self.compute_evaluation_signature(eval_result)
        if sig in self._processed_signatures:
            result.update({
                "status": "REJECTED_DUPLICATE_INCIDENT",
                "error": f"Duplicate incident detected with signature '{sig[:12]}...'. Re-execution blocked.",
            })
            return result

        # 3. Provenance & Schema Validation via FunctionalDiagnosticAdapter
        valid, rejection_reason, result_tuple = FunctionalDiagnosticAdapter.validate_and_convert_failure(
            eval_result,
            workspace_root=self.project_root,
        )
        if not valid or not result_tuple:
            result.update({
                "status": "REJECTED_PROVENANCE_OR_INTEGRITY",
                "error": rejection_reason or "Unknown provenance validation failure.",
            })
            return result

        event, incident, evidence = result_tuple

        # Strict identity check: mission_id must be None
        if incident.mission_id is not None:
            result.update({
                "status": "CONTRACT_MISMATCH",
                "error": "IncidentRecord contains an unauthorized client mission_id.",
            })
            return result

        # Mark signature as processed to prevent replay
        self._processed_signatures.add(sig)
        result["internal_incident_admitted"] = True
        result["incident_id"] = incident.incident_id

        # 4. Diagnostic Pipeline (Read-Only Analysis)
        inc, ev, trace, root_cause, proposal = self.engine.process_incident(
            failure_type=incident.failure_type,
            observed_symptom=incident.observed_symptom,
            source_component=incident.source_component,
            initial_evidence=incident.initial_evidence,
            source_files=[eval_result.target_file_rel],
            mission_id=None,
            instance_id=None,
            workspace_root=self.project_root,
            persist_artifacts=persist_artifacts,
        )

        result["trace_complete"] = trace.trace_complete if trace else False
        result["root_cause_proven"] = root_cause.proven
        result["responsible_function"] = root_cause.responsible_function

        # 5. Root Cause Confidence Gate
        if not root_cause.proven or root_cause.root_cause == "UNPROVEN":
            result.update({
                "status": "ROOT_CAUSE_UNPROVEN",
                "error": "Root cause analysis failed to prove a definitive defect. Patching blocked.",
            })
            return result

        # 6. Read-Only Gate
        if read_only:
            result.update({
                "status": "READ_ONLY_DIAGNOSTIC_COMPLETE",
                "proposal_generated": True,
            })
            return result

        # 7. Patch Plan Acquisition
        patch_plan = None
        if explicit_patch_instruction:
            # Construct PatchPlan directly from verified instruction
            op_raw = str(explicit_patch_instruction.get("operation") or "REPLACE_EXACT").strip().upper()
            if op_raw in ("SAFE_REPLACE", "REPLACE_EXACT"):
                op = PatchOperationType.REPLACE_EXACT
            elif op_raw in ("INSERT_AFTER",):
                op = PatchOperationType.INSERT_AFTER
            elif op_raw in ("INSERT_BEFORE",):
                op = PatchOperationType.INSERT_BEFORE
            else:
                op = PatchOperationType.REPLACE_EXACT

            before_str = str(explicit_patch_instruction.get("before") or explicit_patch_instruction.get("search_block") or "")
            after_str = str(explicit_patch_instruction.get("after") or explicit_patch_instruction.get("replace_block") or "")

            inst = PatchInstruction(
                file_rel_path=str(explicit_patch_instruction.get("file_rel_path") or eval_result.target_file_rel),
                operation=op,
                before=before_str,
                after=after_str,
                reason=str(explicit_patch_instruction.get("reason") or "Internal autonomous fix"),
            )
            patch_plan = PatchPlan(
                incident_id=incident.incident_id,
                repair_id=proposal.repair_id,
                instructions=[inst],
            )
        else:
            # Attempt autonomous reasoning via engine's reasoner
            reasoning_res = self.engine.patch_reasoner.reason_patch(
                event=event,
                incident=incident,
                evidence=evidence,
                root_cause_info=root_cause,
                proposal=proposal,
                workspace_root=self.project_root,
            )
            if reasoning_res.success and reasoning_res.confidence == "HIGH" and reasoning_res.candidate_instructions:
                plan_res = self.engine.patch_plan_generator.generate_plan(
                    event=event,
                    root_cause_info=root_cause,
                    proposal=proposal,
                    workspace_root=self.project_root,
                    explicit_patch_instruction=reasoning_res.candidate_instructions[0].to_dict(),
                )
                if plan_res.success and plan_res.plan:
                    patch_plan = plan_res.plan

        if not patch_plan:
            result.update({
                "status": "PATCH_PLAN_UNAVAILABLE",
                "error": "Autonomous patch reasoner could not synthesize a high-confidence plan, and no explicit instruction was provided.",
            })
            return result

        # 8. Sandbox Repair & Independent Verification Execution
        manifest, patch_res, verif, comp, verdict, promotion = self.engine.execute_sandbox_repair(
            incident=incident,
            evidence=evidence,
            root_cause=root_cause,
            proposal=proposal,
            patch_plan=patch_plan,
            focused_test_runner=focused_test_runner,
            regression_test_runner=regression_test_runner,
            persist_artifacts=persist_artifacts,
            workspace_root=self.project_root,
        )

        result.update({
            "status": "SANDBOX_REPAIR_COMPLETE",
            "sandbox_repair_executed": True,
            "patch_status": patch_res.status,
            "candidate_verdict": verdict.verdict,
            "promotion_proposed": promotion is not None,
            "candidate_dir": manifest.candidate_dir,
            "production_write_authority": False,
            "auto_promotion": False,
        })

        return result
