"""Inheritance Engine — promote to global pool and query inherited memories.

Core operations:
- promote_to_global(): copy qualifying semantic memory to global pool
- query_global_pool(): search global pool for inherited memories
- get_global_stats(): pool statistics
- get_lineage(): trace memory origin
- sync_all_local(): re-evaluate all local semantics
"""

import json
import logging
from datetime import datetime, timezone
from uuid import uuid4

from soagila.core.inheritance.policies import (
    should_promote_to_global,
    calculate_inherited_confidence,
    get_default_policy,
)

logger = logging.getLogger("soagila.inheritance.engine")


async def pin_episodic_to_global(
    episodic_memory_id: str,
    project_hash: str,
    project_name: str | None = None,
    pattern_description: str | None = None,
) -> dict:
    """Pin an episodic memory directly into the global semantic pool.

    The standard ``promote_to_global`` flow demands a memory that has
    already climbed to the semantic layer through repeated reinforcement.
    For user-identity facts ("Sonat speaks Turkish", "always reply
    concisely") that's the wrong shape -- we want them in the global
    pool the moment the user states them, not after ten repetitions.

    This helper bypasses the heuristic gate. It reads the episodic row
    from the active project DB, then writes a row into
    global_semantic_memories with high confidence and inheritance_depth
    1. Used by soagila_remember when scope="global".
    """
    from soagila.db.sqlite_init import get_db, get_global_db

    db = await get_db()
    cursor = await db.execute(
        """SELECT id, org_id, user_id, encrypted_content, encryption_key_ref,
                  topic_tags, classification, confidence_score, created_at
           FROM episodic_memories WHERE id = ?""",
        (episodic_memory_id,),
    )
    row = await cursor.fetchone()
    if not row:
        return {"status": "skipped", "reason": "episodic not found"}

    org_id, user_id = row[1], row[2]
    encrypted = row[3]
    key_ref = row[4] or ""
    confidence = float(row[7] or 0.95)
    importance = max(confidence, 0.95)
    created_at = row[8] or datetime.now(timezone.utc).isoformat()

    global_db = await get_global_db()
    global_id = str(uuid4())
    now = datetime.now(timezone.utc).isoformat()

    try:
        await global_db.execute(
            """INSERT INTO global_semantic_memories
               (id, source_project_hash, source_project_name, source_memory_id,
                org_id, user_id, encrypted_content, encryption_key_ref,
                embedding, pattern_description, confidence_score, original_confidence,
                source_episodic_count, pii_verified_clean, inheritance_depth,
                inherited_at, created_at)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
            (
                global_id, project_hash, project_name or "", episodic_memory_id,
                org_id, user_id, encrypted, key_ref,
                None,  # embedding -- will be populated later by embedding service
                pattern_description or "(global pin)",  # used by recall keyword fallback
                importance, importance,
                1,  # source_episodic_count: this single memory
                1,  # pii_verified_clean (caller is responsible)
                1,  # inheritance_depth
                now, created_at,
            ),
        )
        await global_db.commit()
    except Exception as e:
        return {"status": "error", "reason": str(e)}

    logger.info(
        f"Pinned episodic to global: {episodic_memory_id[:8]}... "
        f"-> {global_id[:8]}... (project {project_hash[:8]})"
    )

    return {
        "status": "pinned",
        "global_id": global_id,
        "source_episodic_id": episodic_memory_id,
        "confidence": importance,
    }


async def promote_to_global(
    semantic_memory_id: str,
    project_hash: str,
    project_name: str = "",
) -> dict:
    """Copy a qualifying semantic memory from project DB to global pool.

    Returns dict with status and details.
    """
    from soagila.db.sqlite_init import get_db, get_global_db

    # Read the semantic memory from project-local DB
    db = await get_db()
    cursor = await db.execute(
        """SELECT id, org_id, user_id, encrypted_content, encryption_key_ref,
                  embedding, pattern_description, confidence_score,
                  source_episodic_count, pii_verified_clean, created_at
           FROM semantic_memories WHERE id = ?""",
        (semantic_memory_id,),
    )
    row = await cursor.fetchone()
    if not row:
        return {"status": "skipped", "reason": "Memory not found"}

    confidence = row[7] or 0.0
    pii_clean = bool(row[9])

    # Get topic tags
    tcursor = await db.execute(
        "SELECT topic_name FROM memory_topics WHERE memory_id = ?",
        (semantic_memory_id,),
    )
    topic_rows = await tcursor.fetchall()
    topics = [r[0] for r in topic_rows]

    # Check eligibility
    qualifies, reason = should_promote_to_global(confidence, pii_clean, topics)
    if not qualifies:
        return {"status": "skipped", "reason": reason}

    # Calculate inherited confidence
    inherited_confidence = calculate_inherited_confidence(confidence, depth=1)

    # Insert into global pool
    global_db = await get_global_db()
    global_id = str(uuid4())
    now = datetime.now(timezone.utc).isoformat()

    try:
        await global_db.execute(
            """INSERT OR IGNORE INTO global_semantic_memories
               (id, source_project_hash, source_project_name, source_memory_id,
                org_id, user_id, encrypted_content, encryption_key_ref,
                embedding, pattern_description, confidence_score, original_confidence,
                source_episodic_count, pii_verified_clean, inheritance_depth,
                inherited_at, created_at)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
            (
                global_id, project_hash, project_name, semantic_memory_id,
                row[1], row[2], row[3], row[4],  # org_id, user_id, content, key_ref
                row[5], row[6],  # embedding, pattern_description
                inherited_confidence, confidence,  # degraded, original
                row[8], 1,  # source_episodic_count, pii_verified_clean
                1, now, row[10],  # depth, inherited_at, created_at
            ),
        )

        # Copy topics
        for topic in topics:
            await global_db.execute(
                "INSERT OR IGNORE INTO global_memory_topics (memory_id, topic_name) VALUES (?, ?)",
                (global_id, topic),
            )

        await global_db.commit()
    except Exception as e:
        # UNIQUE constraint = already promoted, that's fine
        if "UNIQUE" in str(e):
            return {"status": "already_promoted", "source_memory_id": semantic_memory_id}
        raise

    logger.info(
        f"Promoted to global: {semantic_memory_id[:8]}... "
        f"(confidence {confidence:.2f} → {inherited_confidence:.2f}, "
        f"project {project_hash[:8]}...)"
    )

    # Publish event
    from soagila.db.event_bus import publish_event
    await publish_event("soagila:metrics", json.dumps({
        "type": "memory_inherited_to_global",
        "memory_id": semantic_memory_id,
        "global_id": global_id,
        "project_hash": project_hash,
        "confidence": inherited_confidence,
    }))

    return {
        "status": "promoted",
        "global_id": global_id,
        "source_memory_id": semantic_memory_id,
        "confidence": inherited_confidence,
        "original_confidence": confidence,
        "project": project_name or project_hash[:8],
    }


async def query_global_pool(
    query_text: str,
    current_project_hash: str,
    limit: int = 5,
) -> list[dict]:
    """Search the global semantic pool for inherited memories.

    Excludes memories from the current project (you already have those locally).
    Returns list of dicts with inherited=True marker.
    """
    from soagila.db.sqlite_init import get_global_db
    from soagila.core.tcmg.embedding_service import generate_embedding, cosine_similarity, is_available

    try:
        global_db = await get_global_db()
    except Exception:
        return []

    # Get all global memories NOT from current project
    cursor = await global_db.execute(
        """SELECT id, source_project_hash, source_project_name, source_memory_id,
                  pattern_description, confidence_score, original_confidence,
                  embedding, source_episodic_count, inheritance_depth, inherited_at, created_at
           FROM global_semantic_memories
           WHERE source_project_hash != ?
           ORDER BY confidence_score DESC
           LIMIT 50""",
        (current_project_hash,),
    )
    rows = await cursor.fetchall()

    if not rows:
        return []

    # Score by similarity to query
    query_embedding = generate_embedding(query_text) if is_available() else None
    query_words = {w.lower() for w in query_text.split() if len(w) > 2}

    scored = []
    for row in rows:
        mem_id = row[0]
        pattern = row[4] or ""
        embedding_json = row[7]
        similarity = 0.0

        if query_embedding and embedding_json:
            try:
                mem_embedding = json.loads(embedding_json)
                similarity = cosine_similarity(query_embedding, mem_embedding)
            except (json.JSONDecodeError, TypeError):
                pass
        elif query_words:
            # Keyword fallback
            matched = sum(1 for w in query_words if w in pattern.lower())
            similarity = matched / max(len(query_words), 1) * 0.5

        if similarity < 0.2:
            continue

        # Get topics
        tcursor = await global_db.execute(
            "SELECT topic_name FROM global_memory_topics WHERE memory_id = ?",
            (mem_id,),
        )
        topic_rows = await tcursor.fetchall()
        topics = [r[0] for r in topic_rows]

        scored.append({
            "id": mem_id,
            "content": pattern,
            "pattern_description": pattern,
            "topic_tags": topics,
            "confidence_score": row[5],
            "original_confidence": row[6],
            "similarity": round(similarity, 3),
            "relevance": round(similarity * row[5], 3),
            "source_project": row[1],
            "source_project_name": row[2] or row[1][:8] + "...",
            "source_memory_id": row[3],
            "source_episodic_count": row[8],
            "inheritance_depth": row[9],
            "inherited_at": row[10],
            "created_at": row[11],
            "inherited": True,
            "tier": "semantic",
        })

    # Sort by relevance (similarity * confidence)
    scored.sort(key=lambda m: m["relevance"], reverse=True)
    return scored[:limit]


async def get_global_stats() -> dict:
    """Return counts and breakdown of the global pool."""
    from soagila.db.sqlite_init import get_global_db
    from soagila.config import get_project_hash

    try:
        global_db = await get_global_db()
    except Exception:
        return {"total": 0, "from_current": 0, "from_others": 0, "projects": []}

    current_hash = get_project_hash()

    cursor = await global_db.execute("SELECT COUNT(*) FROM global_semantic_memories")
    total = (await cursor.fetchone())[0]

    cursor = await global_db.execute(
        "SELECT COUNT(*) FROM global_semantic_memories WHERE source_project_hash = ?",
        (current_hash,),
    )
    from_current = (await cursor.fetchone())[0]

    # Projects breakdown
    cursor = await global_db.execute(
        """SELECT source_project_hash, source_project_name, COUNT(*) as cnt
           FROM global_semantic_memories
           GROUP BY source_project_hash
           ORDER BY cnt DESC"""
    )
    project_rows = await cursor.fetchall()
    projects = [
        {"hash": r[0], "name": r[1] or r[0][:8] + "...", "count": r[2]}
        for r in project_rows
    ]

    return {
        "total": total,
        "from_current": from_current,
        "from_others": total - from_current,
        "projects": projects,
    }


async def get_lineage(memory_id: str) -> dict:
    """Trace a memory back to its originating project."""
    from soagila.db.sqlite_init import get_global_db

    try:
        global_db = await get_global_db()
    except Exception:
        return {"found": False, "error": "Global DB not available"}

    cursor = await global_db.execute(
        """SELECT id, source_project_hash, source_project_name, source_memory_id,
                  confidence_score, original_confidence, inheritance_depth,
                  inherited_at, created_at
           FROM global_semantic_memories WHERE id = ? OR source_memory_id = ?""",
        (memory_id, memory_id),
    )
    row = await cursor.fetchone()

    if not row:
        return {"found": False, "memory_id": memory_id}

    return {
        "found": True,
        "global_id": row[0],
        "source_project_hash": row[1],
        "source_project_name": row[2] or row[1][:8] + "...",
        "source_memory_id": row[3],
        "confidence_score": row[4],
        "original_confidence": row[5],
        "inheritance_depth": row[6],
        "inherited_at": row[7],
        "created_at": row[8],
    }


async def sync_all_local() -> dict:
    """Re-evaluate all local semantic memories for global promotion."""
    from soagila.db.sqlite_init import get_db
    from soagila.config import get_project_hash, get_project_dir
    import json as _json

    db = await get_db()
    project_hash = get_project_hash()

    # Get project name
    project_name = ""
    try:
        meta_path = get_project_dir() / "project.json"
        if meta_path.exists():
            project_name = _json.loads(meta_path.read_text()).get("name", "")
    except Exception:
        pass

    cursor = await db.execute(
        "SELECT id FROM semantic_memories WHERE pii_verified_clean = 1"
    )
    rows = await cursor.fetchall()

    promoted = 0
    skipped = 0
    already = 0

    for row in rows:
        result = await promote_to_global(row[0], project_hash, project_name)
        status = result.get("status", "")
        if status == "promoted":
            promoted += 1
        elif status == "already_promoted":
            already += 1
        else:
            skipped += 1

    return {
        "total_evaluated": len(rows),
        "promoted": promoted,
        "already_in_global": already,
        "skipped": skipped,
        "project_hash": project_hash,
        "project_name": project_name,
    }
