"""Snowflake persistence for discrepancy runtime events."""

from __future__ import annotations

import json
import os
from typing import Any, Dict, Tuple


class SnowflakeDiscrepancyStore:
    """Best-effort Snowflake upsert adapter.

    Adapter is optional: if the Snowflake connector is unavailable or
    env vars are missing, caller can gracefully fall back.
    """

    def __init__(self) -> None:
        self._connector = None
        self._init_error = ""
        try:
            import snowflake.connector  # type: ignore

            self._connector = snowflake.connector
        except Exception as exc:
            self._init_error = str(exc)

    def available(self) -> Tuple[bool, str]:
        if self._connector is None:
            return False, f"snowflake_connector_unavailable: {self._init_error}"
        required = [
            "SNOWFLAKE_ACCOUNT",
            "SNOWFLAKE_USER",
            "SNOWFLAKE_PASSWORD",
            "SNOWFLAKE_WAREHOUSE",
            "SNOWFLAKE_DATABASE",
            "SNOWFLAKE_SCHEMA",
        ]
        missing = [k for k in required if not os.getenv(k, "").strip()]
        if missing:
            return False, f"snowflake_env_missing: {','.join(missing)}"
        return True, ""

    def _connect(self):
        assert self._connector is not None
        return self._connector.connect(
            account=os.getenv("SNOWFLAKE_ACCOUNT", ""),
            user=os.getenv("SNOWFLAKE_USER", ""),
            password=os.getenv("SNOWFLAKE_PASSWORD", ""),
            role=os.getenv("SNOWFLAKE_ROLE", ""),
            warehouse=os.getenv("SNOWFLAKE_WAREHOUSE", ""),
            database=os.getenv("SNOWFLAKE_DATABASE", ""),
            schema=os.getenv("SNOWFLAKE_SCHEMA", ""),
        )

    def ensure_table(self, conn) -> None:
        table = os.getenv("RAG_DISCREPANCY_TABLE", "DISCREPANCY_EVENTS")
        ddl = f"""
        CREATE TABLE IF NOT EXISTS {table} (
            EVENT_KEY STRING,
            CASE_ID STRING,
            TENANT_ID STRING,
            SOURCE_SYSTEM STRING,
            METRIC STRING,
            SYMPTOM STRING,
            VERIFICATION STRING,
            ROOT_CAUSE STRING,
            FIX_TYPE STRING,
            CONFIDENCE FLOAT,
            RISK_TIER STRING,
            EVENT_TS NUMBER,
            INGESTED_AT NUMBER,
            TAGS VARIANT,
            LINEAGE_PATH VARIANT,
            METADATA VARIANT,
            RAW_EVENT VARIANT,
            INDEX_UPDATED BOOLEAN,
            TRUST_SCORE FLOAT,
            TRUST_FACTORS VARIANT,
            POLICY_DECISION STRING,
            POLICY_REASON_CODES VARIANT,
            POLICY_MODE STRING,
            RETENTION_TIER STRING,
            PRIMARY KEY (EVENT_KEY)
        )
        """
        with conn.cursor() as cur:
            cur.execute(ddl)

    def upsert_event(self, event: Dict[str, Any], *, index_updated: bool) -> Tuple[bool, str]:
        ok, reason = self.available()
        if not ok:
            return False, reason
        table = os.getenv("RAG_DISCREPANCY_TABLE", "DISCREPANCY_EVENTS")
        merge_sql = f"""
        MERGE INTO {table} tgt
        USING (
            SELECT
              %(event_key)s::STRING AS EVENT_KEY,
              %(case_id)s::STRING AS CASE_ID,
              %(tenant_id)s::STRING AS TENANT_ID,
              %(source_system)s::STRING AS SOURCE_SYSTEM,
              %(metric)s::STRING AS METRIC,
              %(symptom)s::STRING AS SYMPTOM,
              %(verification)s::STRING AS VERIFICATION,
              %(root_cause)s::STRING AS ROOT_CAUSE,
              %(fix)s::STRING AS FIX_TYPE,
              %(confidence)s::FLOAT AS CONFIDENCE,
              %(risk_tier)s::STRING AS RISK_TIER,
              %(event_ts)s::NUMBER AS EVENT_TS,
              %(ingested_at)s::NUMBER AS INGESTED_AT,
              PARSE_JSON(%(tags)s) AS TAGS,
              PARSE_JSON(%(lineage_path)s) AS LINEAGE_PATH,
              PARSE_JSON(%(metadata)s) AS METADATA,
              PARSE_JSON(%(raw_event)s) AS RAW_EVENT,
              %(index_updated)s::BOOLEAN AS INDEX_UPDATED,
              %(trust_score)s::FLOAT AS TRUST_SCORE,
              PARSE_JSON(%(trust_factors)s) AS TRUST_FACTORS,
              %(policy_decision)s::STRING AS POLICY_DECISION,
              PARSE_JSON(%(policy_reason_codes)s) AS POLICY_REASON_CODES,
              %(policy_mode)s::STRING AS POLICY_MODE,
              %(retention_tier)s::STRING AS RETENTION_TIER
        ) src
        ON tgt.EVENT_KEY = src.EVENT_KEY
        WHEN MATCHED THEN UPDATE SET
          CASE_ID = src.CASE_ID,
          TENANT_ID = src.TENANT_ID,
          SOURCE_SYSTEM = src.SOURCE_SYSTEM,
          METRIC = src.METRIC,
          SYMPTOM = src.SYMPTOM,
          VERIFICATION = src.VERIFICATION,
          ROOT_CAUSE = src.ROOT_CAUSE,
          FIX_TYPE = src.FIX_TYPE,
          CONFIDENCE = src.CONFIDENCE,
          RISK_TIER = src.RISK_TIER,
          EVENT_TS = src.EVENT_TS,
          INGESTED_AT = src.INGESTED_AT,
          TAGS = src.TAGS,
          LINEAGE_PATH = src.LINEAGE_PATH,
          METADATA = src.METADATA,
          RAW_EVENT = src.RAW_EVENT,
          INDEX_UPDATED = src.INDEX_UPDATED,
          TRUST_SCORE = src.TRUST_SCORE,
          TRUST_FACTORS = src.TRUST_FACTORS,
          POLICY_DECISION = src.POLICY_DECISION,
          POLICY_REASON_CODES = src.POLICY_REASON_CODES,
          POLICY_MODE = src.POLICY_MODE,
          RETENTION_TIER = src.RETENTION_TIER
        WHEN NOT MATCHED THEN INSERT (
          EVENT_KEY, CASE_ID, TENANT_ID, SOURCE_SYSTEM, METRIC, SYMPTOM,
          VERIFICATION, ROOT_CAUSE, FIX_TYPE, CONFIDENCE, RISK_TIER, EVENT_TS,
          INGESTED_AT, TAGS, LINEAGE_PATH, METADATA, RAW_EVENT, INDEX_UPDATED,
          TRUST_SCORE, TRUST_FACTORS, POLICY_DECISION, POLICY_REASON_CODES, POLICY_MODE, RETENTION_TIER
        ) VALUES (
          src.EVENT_KEY, src.CASE_ID, src.TENANT_ID, src.SOURCE_SYSTEM, src.METRIC, src.SYMPTOM,
          src.VERIFICATION, src.ROOT_CAUSE, src.FIX_TYPE, src.CONFIDENCE, src.RISK_TIER, src.EVENT_TS,
          src.INGESTED_AT, src.TAGS, src.LINEAGE_PATH, src.METADATA, src.RAW_EVENT, src.INDEX_UPDATED,
          src.TRUST_SCORE, src.TRUST_FACTORS, src.POLICY_DECISION, src.POLICY_REASON_CODES, src.POLICY_MODE, src.RETENTION_TIER
        )
        """
        params = {
            "event_key": event.get("event_key", ""),
            "case_id": event.get("case_id", ""),
            "tenant_id": event.get("tenant_id", "default"),
            "source_system": event.get("source_system", ""),
            "metric": event.get("metric", ""),
            "symptom": event.get("symptom", ""),
            "verification": event.get("verification", "pending"),
            "root_cause": event.get("root_cause", "unknown"),
            "fix": event.get("fix", "pending"),
            "confidence": float(event.get("confidence", 0.0) or 0.0),
            "risk_tier": event.get("risk_tier", "unknown"),
            "event_ts": int(event.get("event_ts", 0) or 0),
            "ingested_at": int(event.get("ingested_at", 0) or 0),
            "tags": json.dumps(event.get("tags", []) or []),
            "lineage_path": json.dumps(event.get("lineage_path", []) or []),
            "metadata": json.dumps(event.get("metadata", {}) or {}),
            "raw_event": json.dumps(event.get("raw_event", {}) or {}),
            "index_updated": bool(index_updated),
            "trust_score": float(event.get("trust_score", 0.0) or 0.0),
            "trust_factors": json.dumps(event.get("trust_factors", {}) or {}),
            "policy_decision": event.get("policy_decision", "unknown"),
            "policy_reason_codes": json.dumps(event.get("policy_reason_codes", []) or []),
            "policy_mode": event.get("policy_mode", "shadow"),
            "retention_tier": event.get("retention_tier", "hot"),
        }
        try:
            conn = self._connect()
            self.ensure_table(conn)
            with conn.cursor() as cur:
                cur.execute(merge_sql, params)
            conn.commit()
            conn.close()
            return True, ""
        except Exception as exc:
            return False, f"snowflake_upsert_failed: {exc}"
