"""
Belief Revision System - Discovery Engine

Cross-domain analog discovery and proposal generation.
Builds mesh indices and suggests potential belief transfers.
"""
from __future__ import annotations

import datetime
from dataclasses import dataclass
from typing import Any, Dict, List, Optional, Sequence, Tuple

from .storage import CASStore
from .mesh import (
    MeshIndex, PatternSignature, build_mesh_index,
    propose_analogs, AnalogProposal, SimilarityMetric,
    DEFAULT_SIMILARITY
)
from .revision import make_analog_proposal


# =============================================================================
# Domain Specification
# =============================================================================

@dataclass(frozen=True)
class DomainSpec:
    """Specification for a domain world to include in discovery."""
    domain_id: str
    world_label: str = "green"

    def as_tuple(self) -> Tuple[str, str]:
        return (self.domain_id, self.world_label)


# =============================================================================
# Analog Suggestion
# =============================================================================

def suggest_analogs(
    store: CASStore,
    domains: Sequence[DomainSpec],
    top_k: int = 20,
    min_score: float = 0.35,
    similarity: SimilarityMetric = DEFAULT_SIMILARITY
) -> List[Dict[str, Any]]:
    """
    Suggest cross-domain analog patterns.

    Builds a mesh index and returns formatted suggestions
    suitable for display or further processing.

    Args:
        store: Storage backend
        domains: Domains to include in discovery
        top_k: Maximum suggestions
        min_score: Minimum similarity threshold
        similarity: Similarity metric to use

    Returns:
        List of analog suggestions as dicts
    """
    idx = build_mesh_index(store, [d.as_tuple() for d in domains])
    props = propose_analogs(idx.signatures, top_k=top_k, min_score=min_score)

    out = []
    for p in props:
        out.append({
            "score": p.score,
            "left": f"{p.left_domain}:{p.left_name} ({p.left_pattern_id[:12]}...)",
            "right": f"{p.right_domain}:{p.right_name} ({p.right_pattern_id[:12]}...)",
            "left_domain": p.left_domain,
            "left_pattern_id": p.left_pattern_id,
            "right_domain": p.right_domain,
            "right_pattern_id": p.right_pattern_id,
            "shared_operators": list(p.shared_operators),
            "shared_tokens": list(p.shared_tokens)[:20],
        })
    return out


def persist_analog_proposals(
    store: CASStore,
    domains: Sequence[DomainSpec],
    top_k: int = 20,
    min_score: float = 0.35,
    similarity: SimilarityMetric = DEFAULT_SIMILARITY,
    status: str = "new"
) -> List[str]:
    """
    Generate and persist analog proposals into the queue.

    Args:
        store: Storage backend
        domains: Domains to include in discovery
        top_k: Maximum proposals
        min_score: Minimum similarity threshold
        similarity: Similarity metric to use
        status: Initial proposal status

    Returns:
        List of proposal IDs
    """
    idx = build_mesh_index(store, [d.as_tuple() for d in domains])
    props = propose_analogs(idx.signatures, top_k=top_k, min_score=min_score)

    ids: List[str] = []
    for p in props:
        payload = {
            "left_domain": p.left_domain,
            "left_pattern_id": p.left_pattern_id,
            "left_name": p.left_name,
            "right_domain": p.right_domain,
            "right_pattern_id": p.right_pattern_id,
            "right_name": p.right_name,
            "score": p.score,
            "shared_tokens": list(p.shared_tokens),
            "shared_operators": list(p.shared_operators),
        }
        ap = make_analog_proposal(payload)
        pid = store.put_proposal("AnalogProposal", ap, status=status, notes="")
        ids.append(pid)

    return ids


# =============================================================================
# Targeted Discovery
# =============================================================================

def find_analogs_for_pattern(
    store: CASStore,
    domain_id: str,
    pattern_id: str,
    other_domains: Sequence[DomainSpec],
    top_k: int = 10,
    min_score: float = 0.3,
    similarity: SimilarityMetric = DEFAULT_SIMILARITY
) -> List[Dict[str, Any]]:
    """
    Find analogs for a specific pattern from other domains.

    Args:
        store: Storage backend
        domain_id: Domain of the target pattern
        pattern_id: Pattern to find analogs for
        other_domains: Domains to search
        top_k: Maximum results
        min_score: Minimum similarity threshold
        similarity: Similarity metric

    Returns:
        List of analog suggestions
    """
    # Build index of other domains
    idx = build_mesh_index(store, [d.as_tuple() for d in other_domains])

    # Get target pattern
    try:
        obj = store.get_object(pattern_id)
        target_sig = PatternSignature.from_pattern(domain_id, obj["json"])
    except KeyError:
        # Try scanning patterns
        import json as _json
        cur = store._conn.cursor()
        rows = cur.execute("SELECT json FROM objects WHERE kind='Pattern'").fetchall()
        patterns = [_json.loads(r[0]) for r in rows]
        target_pattern = next((p for p in patterns if p.get("id") == pattern_id), None)
        if not target_pattern:
            return []
        target_sig = PatternSignature.from_pattern(domain_id, target_pattern)

    # Find similar patterns
    results = idx.find_similar(
        target_sig,
        top_k=top_k,
        min_score=min_score,
        similarity=similarity,
        exclude_same_domain=True
    )

    out = []
    for sig, score in results:
        shared_tokens = tuple(sorted(set(target_sig.tokens).intersection(sig.tokens)))[:20]
        shared_ops = tuple(sorted(set(target_sig.operators).intersection(sig.operators)))

        out.append({
            "score": score,
            "target": f"{domain_id}:{target_sig.name}",
            "analog": f"{sig.domain_id}:{sig.name} ({sig.pattern_id[:12]}...)",
            "analog_domain": sig.domain_id,
            "analog_pattern_id": sig.pattern_id,
            "shared_tokens": list(shared_tokens),
            "shared_operators": list(shared_ops),
        })

    return out


# =============================================================================
# Domain Statistics
# =============================================================================

def domain_stats(store: CASStore, domain_id: str, world_label: str = "green") -> Dict[str, Any]:
    """
    Get statistics for a domain world.

    Args:
        store: Storage backend
        domain_id: Domain identifier
        world_label: World version

    Returns:
        Statistics dict
    """
    try:
        world = store.get_world(domain_id, world_label)["json"]
    except KeyError:
        return {"error": f"World not found: {domain_id}:{world_label}"}

    return {
        "domain_id": domain_id,
        "world_label": world_label,
        "num_nodes": len(world.get("node_ids", [])),
        "num_edges": len(world.get("edge_ids", [])),
        "num_patterns": len(world.get("pattern_ids", [])),
        "num_evidence": len(world.get("evidence_ids", [])),
        "created_utc": world.get("created_utc"),
        "notes": world.get("notes", ""),
    }


def mesh_stats(store: CASStore, domains: Sequence[DomainSpec]) -> Dict[str, Any]:
    """
    Get statistics for a mesh across domains.

    Args:
        store: Storage backend
        domains: Domains to include

    Returns:
        Mesh statistics
    """
    idx = build_mesh_index(store, [d.as_tuple() for d in domains])

    domain_counts = {}
    for sig in idx.signatures:
        domain_counts[sig.domain_id] = domain_counts.get(sig.domain_id, 0) + 1

    return {
        "total_patterns": len(idx.signatures),
        "domains": list(idx.domains()),
        "patterns_per_domain": domain_counts,
    }
