"""
CrashSense SDK — clean, importable public API.

Usage
-----
::

    from crashsense.sdk import CrashSense

    cs = CrashSense(provider="gemini", chain="ethereum")

    # Diagnose a transaction
    result = cs.diagnose("0xabc…")

    # Diagnose raw error text
    result = cs.diagnose_raw("execution reverted: insufficient balance")

    # Access history
    history = cs.history()
    single  = cs.get_diagnosis("0xabc…")
    matches = cs.search("slippage")
    stats   = cs.stats()

All results are plain dicts — no ORM objects leak outside this module.
"""

from __future__ import annotations

from typing import Any, Dict, List, Optional

from .config import load_config, save_config, SUPPORTED_CHAINS
from .core.memory import MemoryStore
from .core.tx_analyzer import TxAnalyzer
from .core.remediation import RemediationEngine


class CrashSense:
    """
    High-level facade for the CrashSense diagnostic engine.

    Parameters
    ----------
    provider : str
        LLM provider name (``"gemini"`` | ``"openai"`` | ``"claude"`` | ``"ollama"``).
    chain : str
        Default blockchain network (``"ethereum"`` | ``"polygon"`` | …).
    db_path : str | None
        Custom SQLite path. ``None`` → default ``~/.crashsense/memories.db``.
    auto_save : bool
        Automatically persist every diagnosis to the database.
    """

    def __init__(
        self,
        provider: str = "gemini",
        chain: str = "ethereum",
        db_path: str | None = None,
        auto_save: bool = True,
    ) -> None:
        self.provider = provider
        self.chain = chain
        self.auto_save = auto_save
        self._cfg = load_config()
        self._cfg["provider"] = provider
        self._cfg.setdefault("chain", {})["name"] = chain
        self._store = MemoryStore(db_path=db_path)
        self._engine = RemediationEngine()

    # ── Core diagnosis ────────────────────────────────────────────────────

    def diagnose(self, tx_hash: str, *, chain: str | None = None, provider: str | None = None) -> Dict[str, Any]:
        """
        Diagnose a failed/reverted blockchain transaction.

        Returns a dict with keys: ``tx_context``, ``patterns_matched``,
        ``analysis``, ``remediation``.
        """
        prov = provider or self.provider
        ch = chain or self.chain
        cfg = dict(self._cfg)
        cfg.setdefault("chain", {})["name"] = ch
        analyzer = TxAnalyzer(provider=prov, cfg=cfg)
        result = analyzer.diagnose(tx_hash)

        patterns = result.get("patterns_matched", [])
        remediation = self._engine.get_remediation(patterns)
        result["remediation"] = remediation

        if self.auto_save:
            self._persist(tx_hash, result, provider=prov)

        return result

    def diagnose_raw(self, error_text: str, *, provider: str | None = None) -> Dict[str, Any]:
        """
        Diagnose a raw error message / revert string (no on-chain fetch).

        Returns a dict with keys: ``raw_input``, ``patterns_matched``,
        ``analysis``, ``remediation``.
        """
        prov = provider or self.provider
        analyzer = TxAnalyzer(provider=prov, cfg=self._cfg)
        result = analyzer.diagnose_raw(error_text)

        patterns = result.get("patterns_matched", [])
        remediation = self._engine.get_remediation(patterns)
        result["remediation"] = remediation

        if self.auto_save:
            fake_hash = f"raw_{MemoryStore._hash(error_text)[:16]}"
            self._persist(fake_hash, result, provider=prov, raw_input=error_text)

        return result

    # ── History / DB access ───────────────────────────────────────────────

    def history(self, page: int = 1, per_page: int = 50) -> Dict[str, Any]:
        """Return paginated diagnosis history as a plain dict."""
        data = self._store.get_all(page=page, per_page=per_page)
        data["items"] = [self._store.record_to_dict(r) for r in data["items"]]
        return data

    def get_diagnosis(self, tx_hash: str) -> Optional[Dict[str, Any]]:
        """Retrieve a single diagnosis by tx hash, or ``None``."""
        rec = self._store.get_by_tx_hash(tx_hash)
        return self._store.record_to_dict(rec) if rec else None

    def search(self, keyword: str, limit: int = 20) -> List[Dict[str, Any]]:
        """Search diagnoses by keyword."""
        recs = self._store.search(keyword, limit=limit)
        return [self._store.record_to_dict(r) for r in recs]

    def stats(self) -> Dict[str, Any]:
        """Aggregate statistics across all stored diagnoses."""
        return self._store.get_stats()

    def get_actions(self, diagnosis_id: int) -> List[Dict[str, Any]]:
        """Return all AI action logs for a given diagnosis ID."""
        acts = self._store.get_actions(diagnosis_id)
        return [self._store.action_to_dict(a) for a in acts]

    # ── Configuration helpers ─────────────────────────────────────────────

    @staticmethod
    def supported_chains() -> Dict[str, Any]:
        """Return the dict of all supported blockchain networks."""
        return dict(SUPPORTED_CHAINS)

    @staticmethod
    def supported_providers() -> List[str]:
        """Return list of supported LLM provider names."""
        return ["gemini", "openai", "claude", "ollama"]

    # ── Internal ──────────────────────────────────────────────────────────

    def _persist(self, tx_hash: str, result: dict, *, provider: str = "", raw_input: str = ""):
        """Save a full diagnosis + AI action log to the DB."""
        analysis = result.get("analysis", {})
        ctx = result.get("tx_context", {})
        patterns = result.get("patterns_matched", [])
        remediation = result.get("remediation", [])

        rec = self._store.save_diagnosis(
            tx_hash=tx_hash,
            chain=ctx.get("chain", self.chain),
            summary=analysis.get("explanation", ""),
            root_cause=analysis.get("root_cause", ""),
            suggested_fix=analysis.get("suggested_fix", ""),
            explanation=analysis.get("explanation", ""),
            patterns=patterns,
            remediation=remediation,
            provider=provider,
            status=ctx.get("status", ""),
            function_called=ctx.get("function_called", ""),
            from_address=ctx.get("from", ""),
            to_address=ctx.get("to", ""),
            value_eth=ctx.get("value_eth", 0.0),
            gas_used=ctx.get("gas_used", 0),
            gas_limit=ctx.get("gas_limit", 0),
            revert_reason=ctx.get("revert_reason", ""),
            tx_context=ctx,
            analysis=analysis,
            raw_input=raw_input,
        )

        # granular action log
        if patterns:
            self._store.log_action(rec.id, "pattern_match", f"Matched {len(patterns)} patterns", {"patterns": patterns})
        self._store.log_action(rec.id, "llm_call", f"Provider: {provider}", {"analysis": analysis})
        if remediation:
            self._store.log_action(rec.id, "remediation", f"{len(remediation)} fixes", {"remediation": remediation})
