"""Hash-based reconciliation workflow.

Reuses:
    - ``databridge.reconciler.compare_hashes(source_a_path, source_b_path, key_columns, compare_columns)``
    - ``databridge.reconciler.get_orphan_details(...)``
    - ``databridge.reconciler.get_conflict_details(...)``

IKE integration (optional):
    - P1 Knowledge Base: Ingest reconciliation outcomes for institutional memory
    - P2 Fix Engine: Auto-generate fixes for detected conflicts

Graph integration (optional):
    - Lineage: RECON_CHECK node + source edges
    - Evidence: record_recon_outcome() for evidence trail
    - Vector: Index recon summary for semantic search
    - KB Graph: Ingest recon results into knowledge base graph
"""

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 HashReconciliationWorkflow(GraphContextMixin, BaseWorkflow):
    """Compare two data sources using hash-based reconciliation.

    Identifies orphans (rows in one source but not the other) and
    conflicts (rows with matching keys but different values).

    When IKE is available, reconciliation outcomes are ingested
    into IKE's P1 knowledge base, and IKE's P2 fix engine can
    auto-generate conflict resolutions.

    Reuses:
        - ``databridge.reconciler.compare_hashes(source_a_path, source_b_path, key_columns, compare_columns)``
        - ``databridge.reconciler.get_orphan_details(...)``
        - ``databridge.reconciler.get_conflict_details(...)``
    """

    name = "reconciliation.hash_compare"
    category = "reconciliation"
    description = "Hash-based reconciliation between two data sources"
    required_params = ["source_a_path", "source_b_path", "key_columns"]
    optional_params = {
        "compare_columns": "",
        "include_details": True,
        "ike_ingest": True,
        "ike_auto_fix": False,
    }

    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._compare(params, ctx)]
        if params.get("include_details"):
            steps.append(self._orphan_details(params, ctx))
            steps.append(self._conflict_details(params, ctx))
        if ctx and ctx.ike.available:
            # IKE P1: Ingest reconciliation results into KB
            if params.get("ike_ingest"):
                steps.append(self._ike_ingest_results(params, ctx))
            # IKE P2: Auto-generate fixes for conflicts
            if params.get("ike_auto_fix"):
                steps.append(self._ike_auto_fix(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))
        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 Dynamic Table with HASH(key_cols) join + EXCEPT/MINUS pattern",
        )

    @workflow_step("compare_hashes", "Run hash comparison")
    def _compare(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # REUSE: databridge.reconciler.compare_hashes(
        #     source_a_path=params["source_a_path"],
        #     source_b_path=params["source_b_path"],
        #     key_columns=params["key_columns"],
        #     compare_columns=params.get("compare_columns", ""),
        # )
        raise NotImplementedStubError(
            self.name,
            "compare_hashes",
            "databridge.reconciler.compare_hashes(source_a_path, source_b_path, key_columns, compare_columns)",
        )

    @workflow_step("orphan_details", "Get orphan record details")
    def _orphan_details(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # REUSE: databridge.reconciler.get_orphan_details(
        #     source_a_path, source_b_path, key_columns, source="A"
        # )
        raise NotImplementedStubError(
            self.name,
            "orphan_details",
            "databridge.reconciler.get_orphan_details(source_a_path, source_b_path, key_columns)",
        )

    @workflow_step("conflict_details", "Get conflict record details")
    def _conflict_details(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # REUSE: databridge.reconciler.get_conflict_details(
        #     source_a_path, source_b_path, key_columns
        # )
        raise NotImplementedStubError(
            self.name,
            "conflict_details",
            "databridge.reconciler.get_conflict_details(source_a_path, source_b_path, key_columns)",
        )

    @workflow_step("ike_ingest_results", "Ingest recon outcomes into IKE KB")
    def _ike_ingest_results(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # IKE P1: Ingest reconciliation outcomes (orphan counts,
        # conflict patterns, match rates) into institutional KB.
        # Future reconciliations benefit from historical context.
        #
        # ctx.ike.ingest_knowledge(
        #     content=f"Recon {source_a} vs {source_b}: {summary}",
        #     source_type="run_result",
        #     tags=["reconciliation", "hash_compare"],
        # )
        raise NotImplementedStubError(
            self.name,
            "ike_ingest_results",
            "ctx.ike.ingest_knowledge(content, source_type='run_result') — IKE P1 KB ingest",
        )

    @workflow_step("ike_auto_fix", "Auto-generate conflict fixes via IKE P2")
    def _ike_auto_fix(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # IKE P2: For each conflict, generate a fix using the 3-tier
        # fix engine (trust replay -> RAG -> LLM fallback).
        #
        # For each conflict:
        #   ctx.ike.generate_fix(
        #       rule_id="recon_conflict",
        #       finding={"key": conflict_key, "source_a": val_a, "source_b": val_b},
        #   )
        raise NotImplementedStubError(
            self.name,
            "ike_auto_fix",
            "ctx.ike.generate_fix(rule_id='recon_conflict', finding) — IKE P2 3-tier fix engine",
        )

    # --- Graph integration ---

    @workflow_step("graph_record_lineage", "Record RECON_CHECK node and source edges in lineage")
    def _graph_record_lineage(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # ctx.graphs.record_lineage_node("sdk", f"recon:{source_a}_{source_b}", "EXTERNAL")
        # ctx.graphs.record_lineage_edge("sdk", source_a_node, recon_node, "DIRECT")
        # ctx.graphs.record_lineage_edge("sdk", source_b_node, recon_node, "DIRECT")
        raise NotImplementedStubError(
            self.name,
            "graph_record_lineage",
            "ctx.graphs.record_lineage_node/edge — RECON_CHECK + source edges",
        )

    @workflow_step("graph_record_evidence", "Record recon outcome in evidence trail")
    def _graph_record_evidence(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # ctx.graphs.record_recon_evidence(
        #     recon_id=run_id, match_rate=rate, orphans=n,
        #     conflicts=n, source_file=source_a, target_file=source_b,
        # )
        raise NotImplementedStubError(
            self.name,
            "graph_record_evidence",
            "ctx.graphs.record_recon_evidence(recon_id, match_rate, orphans, conflicts, ...)",
        )

    @workflow_step("graph_index_vectors", "Index recon summary in vector store")
    def _graph_index_vectors(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # ctx.graphs.index_content(
        #     doc_id=f"recon_{run_id}", content=summary,
        #     source_type="recon_result", metadata={...},
        # )
        raise NotImplementedStubError(
            self.name,
            "graph_index_vectors",
            "ctx.graphs.index_content(doc_id, content, 'recon_result', metadata)",
        )

    @workflow_step("graph_ingest_kb", "Ingest recon results into KB graph")
    def _graph_ingest_kb(self, params: Dict[str, Any], ctx: Optional[SDKContext] = None):
        # ctx.graphs.ingest_kb_nodes(
        #     nodes=[{"id": recon_id, "type": "RECON_CHECK", "properties": {...}}],
        #     edges=[{"source": source_a, "target": recon_id, "type": "DERIVES_FROM"}],
        # )
        raise NotImplementedStubError(
            self.name,
            "graph_ingest_kb",
            "ctx.graphs.ingest_kb_nodes(nodes, edges) — recon results as KB graph nodes",
        )
