"""Grounded detection workflow — knowledge-base-grounded anomaly detection.

Reuses:
    - ``databridge.detection.detect_grounded(file_path, rules, context, ...)``
    - ``databridge.detection.record_feedback(finding_id, confirmed, ...)``
    - ``databridge.detection.get_detection_stats(feedback_path)``

IKE integration (optional):
    - P4 Scoring: Score each finding with Thompson Sampling + SPRT + Platt
    - P1 Knowledge Base: Ingest confirmed findings into institutional memory
"""

from __future__ import annotations

from typing import Any, Dict, List, Optional

from databridge_core.sdk.core.context import SDKContext
from databridge_core.sdk.core.exceptions import NotImplementedStubError
from databridge_core.sdk.core.graph_mixin import GraphContextMixin
from databridge_core.sdk.core.types import CompiledArtifact, Result
from databridge_core.sdk.workflows.base import BaseWorkflow
from databridge_core.sdk.workflows.result import WorkflowResult
from databridge_core.sdk.workflows.step import workflow_step


class GroundedDetectionWorkflow(GraphContextMixin, BaseWorkflow):
    """Run knowledge-base-grounded anomaly detection on a data file.

    Applies detection rules from the knowledge base, uses Thompson
    Sampling for adaptive thresholds, and supports SPRT early exit.

    When IKE is available (``ctx.ike.available``), findings are
    scored through IKE's P4 scoring engine for calibrated confidence
    and optionally ingested into IKE's P1 knowledge base.

    Reuses:
        - ``databridge.detection.detect_grounded(file_path, ...)``
        - ``databridge.detection.record_feedback(...)``
        - ``databridge.detection.get_detection_stats(...)``
    """

    name = "detection.grounded_detection"
    category = "detection"
    description = "Knowledge-grounded anomaly detection with adaptive thresholds"
    required_params = ["file_path"]
    optional_params = {
        "min_confidence": 0.0,
        "feedback_strategy": "thompson",
        "early_exit": True,
        "use_graphrag": True,
        "knowledge_dir": "data/knowledge",
        "ike_scoring": True,
    }

    def validate(self, params: Dict[str, Any]) -> Dict[str, Any]:
        self._check_required(params)
        return self._apply_defaults(params)

    def execute(
        self, params: Dict[str, Any], ctx: Optional[SDKContext] = None
    ) -> Result:
        steps = [
            self._detect(params, ctx),
            self._stats(params, ctx),
        ]
        # IKE P4: Score findings if IKE is available
        if params.get("ike_scoring") and ctx and ctx.ike.available:
            steps.append(self._ike_score_findings(params, ctx))
        # Graph integration (via GraphContextMixin)
        gctx = self.collect_graph_context(params, ctx)
        if ctx and ctx.graphs.available:
            steps.append(self._graph_record_lineage(params, ctx))
            steps.append(self._graph_record_evidence(params, ctx))
            steps.append(self._graph_index_vectors(params, ctx))
            steps.append(self._graph_ingest_kb(params, ctx))
            steps.append(self._graph_notify_codex(params, ctx))
        result = WorkflowResult.from_steps(self.name, steps)
        result.metadata["graph_context"] = gctx.to_dict()
        return result

    def compile_to_snowflake(
        self, params: Dict[str, Any], ctx: Optional[SDKContext] = None
    ) -> List[CompiledArtifact]:
        raise NotImplementedStubError(
            self.name,
            "compile_to_snowflake",
            "Generate Snowflake Task calling SPCS detection endpoint, "
            "or Snowpark Python UDF wrapping detect_grounded()",
        )

    @workflow_step("detect", "Run grounded detection")
    def _detect(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # REUSE: databridge.detection.detect_grounded(
        #     file_path=params["file_path"],
        #     min_confidence=params["min_confidence"],
        #     feedback_strategy=params["feedback_strategy"],
        #     early_exit=params["early_exit"],
        #     use_graphrag=params["use_graphrag"],
        #     knowledge_dir=params["knowledge_dir"],
        # )
        raise NotImplementedStubError(
            self.name,
            "detect",
            "databridge.detection.detect_grounded(file_path, min_confidence, feedback_strategy, early_exit, ...)",
        )

    @workflow_step("stats", "Get detection statistics")
    def _stats(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # REUSE: databridge.detection.get_detection_stats(feedback_path)
        raise NotImplementedStubError(
            self.name,
            "stats",
            "databridge.detection.get_detection_stats(feedback_path)",
        )

    @workflow_step("ike_score_findings", "Score findings via IKE P4")
    def _ike_score_findings(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # IKE P4: Score each detection finding through Thompson Sampling +
        # SPRT + Platt calibration for calibrated confidence scores.
        #
        # For each finding from the detect step:
        #   ctx.ike.score(
        #       bandit_id=f"detection_{finding['rule_id']}",
        #       item_id=finding["finding_id"],
        #       raw_confidence=finding["confidence"],
        #   )
        #
        # Batch scoring alternative:
        #   ctx.ike.batch_score([
        #       {"bandit_id": f"detection_{f['rule_id']}",
        #        "item_id": f["finding_id"],
        #        "raw_confidence": f["confidence"]}
        #       for f in findings
        #   ])
        raise NotImplementedStubError(
            self.name,
            "ike_score_findings",
            "ctx.ike.batch_score(findings) — IKE P4 Thompson + SPRT + Platt calibration",
        )

    # --- Graph integration ---

    @workflow_step("graph_record_lineage", "Record Finding→RULE edges in lineage")
    def _graph_record_lineage(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # For each finding:
        #   ctx.graphs.record_lineage_node("sdk", finding_id, "EXTERNAL", description=rule_id)
        #   ctx.graphs.record_lineage_edge("sdk", finding_id, rule_id, "DERIVED")
        raise NotImplementedStubError(
            self.name,
            "graph_record_lineage",
            "ctx.graphs.record_lineage_node/edge — Finding→RULE edges",
        )

    @workflow_step("graph_record_evidence", "Record detection evidence in evidence trail")
    def _graph_record_evidence(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # ctx.graphs.record_evidence(
        #     account_id=finding["account"], hierarchy_node_id=finding["rule_id"],
        #     method="grounded_detection", confidence=finding["confidence"],
        #     evidence_sources=[{"content": finding["description"], ...}],
        # )
        raise NotImplementedStubError(
            self.name,
            "graph_record_evidence",
            "ctx.graphs.record_evidence(account_id, rule_id, 'grounded_detection', confidence, ...)",
        )

    @workflow_step("graph_index_vectors", "Index findings in vector store")
    def _graph_index_vectors(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # ctx.graphs.index_content(
        #     doc_id=f"detection_{finding_id}", content=finding_summary,
        #     source_type="detection_finding", metadata={"rule_id": ...},
        # )
        raise NotImplementedStubError(
            self.name,
            "graph_index_vectors",
            "ctx.graphs.index_content(doc_id, content, 'detection_finding', metadata)",
        )

    @workflow_step("graph_ingest_kb", "Ingest findings into KB graph")
    def _graph_ingest_kb(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # ctx.graphs.ingest_kb_nodes(
        #     nodes=[{"id": finding_id, "type": "RULE", "properties": {...}}],
        # )
        raise NotImplementedStubError(
            self.name,
            "graph_ingest_kb",
            "ctx.graphs.ingest_kb_nodes(nodes) — detection findings as KB nodes",
        )

    @workflow_step("graph_notify_codex", "Ingest discrepancy events into Codex GraphRAG")
    def _graph_notify_codex(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # ctx.graphs.notify_runtime_event({
        #     "event_key": f"detection_{finding_id}",
        #     "source_system": "grounded_detection",
        #     "finding_id": finding_id,
        #     "account": finding["account"],
        #     "risk_tier": finding.get("risk_tier", "unknown"),
        # })
        raise NotImplementedStubError(
            self.name,
            "graph_notify_codex",
            "ctx.graphs.notify_runtime_event(event) — Codex runtime discrepancy ingestion",
        )
