"""
Orchestration Module - High-level consolidation workflows.

Contains:
- run_consolidation: Full consolidation pipeline
- scan_clusters: Preview clusters without processing
"""

from typing import Any, Dict, List

from ..utils.timestamps import utc_now_iso

from .models import Cluster, ConsolidationResult, ConsolidationReport
from .clustering import find_clusters, tier_clusters
from .auto_rules import auto_consolidate_simple
from .claude_client import consolidate_with_claude
from .storage import apply_consolidation
from .logging import log_consolidation

# Maximum clusters to process per batch
MAX_BATCH_SIZE = 10


async def run_consolidation(
    store,
    project: str,
    distance_threshold: float = 0.5,
    max_clusters: int = MAX_BATCH_SIZE,
    preview: bool = False
) -> ConsolidationReport:
    """
    Run full consolidation process on a project.

    Args:
        store: ContextStore instance
        project: Project name
        distance_threshold: Max L2 distance for similarity (default 0.5)
        max_clusters: Max clusters to process (default 10)
        preview: If True, return decisions without applying them

    Returns:
        ConsolidationReport with results
    """
    timestamp = utc_now_iso()

    # Find clusters
    clusters = find_clusters(store, distance_threshold)

    if not clusters:
        return ConsolidationReport(
            project=project,
            timestamp=timestamp,
            clusters_found=0,
            clusters_processed=0,
            consolidated=0,
            kept=0,
            deleted=0
        )

    # Tier clusters
    tiered = tier_clusters(clusters)

    # Process auto-tier first
    results: List[ConsolidationResult] = []

    for cluster in tiered["auto"][:max_clusters]:
        result = auto_consolidate_simple(cluster)
        results.append(result)

    # Budget for LLM processing
    remaining_budget = max_clusters - len(results)

    if remaining_budget > 0:
        # Combine small and large for LLM processing
        llm_clusters = (tiered["llm_small"] + tiered["llm_large"])[:remaining_budget]

        if llm_clusters:
            decisions = await consolidate_with_claude(llm_clusters)

            # Map decisions to clusters
            decision_by_cluster = {d["cluster_id"]: d for d in decisions}

            for cluster in llm_clusters:
                decision = decision_by_cluster.get(cluster.id)
                if decision:
                    if preview:
                        # In preview mode, don't apply - but calculate what WOULD be deleted
                        decision_type = decision.get("decision", "unknown")
                        kept_id = decision.get("keep_id")

                        # Calculate deleted_ids based on decision type
                        if decision_type == "keep_one" and kept_id:
                            # Delete all except the kept one
                            deleted_ids = [mid for mid in cluster.memory_ids if mid != kept_id]
                        elif decision_type == "merge":
                            # Delete all - they'll be replaced by merged memory
                            deleted_ids = list(cluster.memory_ids)
                        else:
                            # keep_all or unknown - don't delete anything
                            deleted_ids = []

                        # Collect files from all memories for merge
                        all_files = []
                        if decision_type == "merge":
                            for mem in cluster.memories:
                                all_files.extend(mem.get("files", []))
                            all_files = list(set(all_files))  # Deduplicate

                        results.append(ConsolidationResult(
                            cluster_id=cluster.id,
                            decision=decision_type,
                            kept_id=kept_id,
                            merged_memory={
                                "type": decision.get("merged_type"),
                                "title": decision.get("merged_title"),
                                "fact": decision.get("merged_fact"),
                                "files": all_files
                            } if decision_type == "merge" else None,
                            deleted_ids=deleted_ids,
                            reasoning=decision.get("reasoning", "")
                        ))
                    else:
                        result = apply_consolidation(store, cluster, decision)
                        results.append(result)
                else:
                    results.append(ConsolidationResult(
                        cluster_id=cluster.id,
                        decision="error",
                        reasoning="No decision returned by LLM"
                    ))

    # If not preview, also apply auto-tier decisions
    if not preview:
        for i, cluster in enumerate(tiered["auto"][:max_clusters]):
            # Results already contain the decision, now apply it
            result = results[i]
            if result.decision == "keep_one" and result.deleted_ids:
                store.delete_memories(result.deleted_ids)

    # Calculate stats
    consolidated = sum(1 for r in results if r.decision == "merge")
    kept = sum(1 for r in results if r.decision in ("keep_one", "keep_all"))
    deleted = sum(len(r.deleted_ids) for r in results)

    report = ConsolidationReport(
        project=project,
        timestamp=timestamp,
        clusters_found=len(clusters),
        clusters_processed=len(results),
        consolidated=consolidated,
        kept=kept,
        deleted=deleted,
        results=results
    )

    # Log unless preview
    if not preview:
        log_consolidation(project, report)

    return report


async def scan_clusters(
    store,
    project: str,
    distance_threshold: float = 0.5
) -> Dict[str, Any]:
    """
    Scan for clusters without processing them.

    Returns cluster information for preview.
    """
    clusters = find_clusters(store, distance_threshold)
    tiered = tier_clusters(clusters)
    total_memories = sum(c.size for c in clusters)

    return {
        "project": project,
        "total_clusters": len(clusters),
        "total_memories": total_memories,
        "clusters": [c.to_dict() for c in clusters],
        "tiers": {
            "auto": len(tiered["auto"]),
            "llm_small": len(tiered["llm_small"]),
            "llm_large": len(tiered["llm_large"])
        }
    }
