"""
Unified Coaching System.

This module combines QualityDelta coaching with V-Emergence insights
to provide comprehensive, actionable recommendations for data improvement.

The coaching system operates on three levels:
1. Field-level: Missing or inadequate field values
2. Relationship-level: Missing or weak connections
3. Emergence-level: Patterns and insights from the knowledge graph

Usage:
    from lightwave.data_quality.coaching import (
        CoachingEngine,
        UnifiedCoachingAdvice,
        generate_unified_coaching,
    )

    # Generate coaching for a task
    engine = CoachingEngine()
    advice = engine.generate_for_entity(task, entity_type="task")

    # Or use the convenience function
    advice = generate_unified_coaching(task, entity_type="task")
"""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime
from decimal import Decimal
from enum import StrEnum
from typing import TYPE_CHECKING, Any

from lightwave.schema.pydantic.models.ideal_state import (
    CoachingAdvice,
    QualityDelta,
    generate_coaching,
)

from .knowledge_graph import (
    KnowledgeEdgeLike,
    compute_node_weight,
)
from .quality_service import (
    compute_document_quality,
    compute_sprint_quality,
    compute_task_quality,
)

if TYPE_CHECKING:
    pass


# =============================================================================
# COACHING CATEGORIES
# =============================================================================


class CoachingCategory(StrEnum):
    """Categories of coaching advice."""

    # From QualityDelta
    MISSING_FIELD = "missing_field"
    CONSTRAINT_VIOLATION = "constraint"
    RELATIONSHIP_GAP = "relationship"
    ENHANCEMENT = "enhancement"

    # From Emergence
    PATTERN = "pattern"
    INSIGHT = "insight"
    CONNECTION = "connection"
    KNOWLEDGE_GAP = "knowledge_gap"


class CoachingUrgency(StrEnum):
    """Urgency levels for coaching advice."""

    CRITICAL = "critical"  # Blocks workflows, must fix
    HIGH = "high"  # Degrades quality significantly
    MEDIUM = "medium"  # Should be addressed
    LOW = "low"  # Nice to have


# =============================================================================
# UNIFIED COACHING STRUCTURES
# =============================================================================


@dataclass
class UnifiedCoachingAdvice:
    """
    Unified coaching advice combining delta and emergence insights.

    Attributes:
        id: Unique identifier for this advice
        priority: Numeric priority (1 = highest)
        urgency: Urgency level
        category: Category of advice
        title: Short title for the advice
        description: Detailed description
        action: Specific action to take
        target_entity: Entity this advice applies to
        target_field: Specific field if applicable
        auto_actionable: Whether AI can take this action
        confidence: Confidence in the recommendation (0-1)
        sources: Where this advice came from
    """

    id: str
    priority: int
    urgency: CoachingUrgency
    category: CoachingCategory
    title: str
    description: str
    action: str
    target_entity: str
    target_field: str = ""
    auto_actionable: bool = False
    confidence: float = 1.0
    sources: list[str] = field(default_factory=list)

    def to_dict(self) -> dict[str, Any]:
        """Serialize to dict."""
        return {
            "id": self.id,
            "priority": self.priority,
            "urgency": self.urgency.value,
            "category": self.category.value,
            "title": self.title,
            "description": self.description,
            "action": self.action,
            "target_entity": self.target_entity,
            "target_field": self.target_field,
            "auto_actionable": self.auto_actionable,
            "confidence": self.confidence,
            "sources": self.sources,
        }


@dataclass
class CoachingReport:
    """
    Complete coaching report for an entity or collection.

    Attributes:
        entity_id: ID of the entity being coached
        entity_type: Type of entity
        computed_at: When the report was generated
        health_score: Overall health score (0-100)
        advice_count: Total number of advice items
        critical_count: Number of critical items
        advice: List of coaching advice
        summary: Human-readable summary
    """

    entity_id: str
    entity_type: str
    computed_at: datetime
    health_score: Decimal
    advice_count: int
    critical_count: int
    advice: list[UnifiedCoachingAdvice]
    summary: str

    def to_dict(self) -> dict[str, Any]:
        """Serialize to dict."""
        return {
            "entity_id": self.entity_id,
            "entity_type": self.entity_type,
            "computed_at": self.computed_at.isoformat(),
            "health_score": float(self.health_score),
            "advice_count": self.advice_count,
            "critical_count": self.critical_count,
            "advice": [a.to_dict() for a in self.advice],
            "summary": self.summary,
        }


# =============================================================================
# COACHING ENGINE
# =============================================================================


class CoachingEngine:
    """
    Engine for generating unified coaching advice.

    Combines QualityDelta analysis with emergence patterns to
    provide comprehensive recommendations.
    """

    def __init__(
        self,
        emergence_edges: list[KnowledgeEdgeLike] | None = None,
        emergence_patterns: list[dict[str, Any]] | None = None,
    ):
        """
        Initialize the coaching engine.

        Args:
            emergence_edges: Optional edges from v_emergence knowledge graph
            emergence_patterns: Optional patterns detected by v_emergence
        """
        self.emergence_edges = emergence_edges or []
        self.emergence_patterns = emergence_patterns or []
        self._advice_counter = 0

    def _next_advice_id(self) -> str:
        """Generate next advice ID."""
        self._advice_counter += 1
        return f"advice_{self._advice_counter}"

    def generate_for_entity(
        self,
        entity: Any,
        entity_type: str,
        max_advice: int = 10,
    ) -> CoachingReport:
        """
        Generate coaching report for a single entity.

        Args:
            entity: Django model instance or entity-like object
            entity_type: One of 'task', 'sprint', 'document'
            max_advice: Maximum number of advice items

        Returns:
            CoachingReport with unified advice
        """
        # Compute quality delta
        if entity_type == "task":
            delta = compute_task_quality(entity)
        elif entity_type == "sprint":
            delta = compute_sprint_quality(entity)
        elif entity_type == "document":
            delta = compute_document_quality(entity)
        else:
            raise ValueError(f"Unknown entity_type: {entity_type}")

        # Generate delta-based coaching
        delta_advice = generate_coaching(delta, max_suggestions=max_advice)

        # Convert to unified format
        unified_advice = self._convert_delta_advice(delta_advice, delta)

        # Add emergence-based advice
        emergence_advice = self._generate_emergence_advice(entity, entity_type)
        unified_advice.extend(emergence_advice)

        # Sort by priority and limit
        unified_advice.sort(key=lambda a: a.priority)
        unified_advice = unified_advice[:max_advice]

        # Count critical items
        critical_count = sum(1 for a in unified_advice if a.urgency == CoachingUrgency.CRITICAL)

        # Generate summary
        summary = self._generate_summary(delta, unified_advice)

        return CoachingReport(
            entity_id=delta.entity_id,
            entity_type=entity_type,
            computed_at=datetime.now(),
            health_score=delta.completeness_score,
            advice_count=len(unified_advice),
            critical_count=critical_count,
            advice=unified_advice,
            summary=summary,
        )

    def generate_for_batch(
        self,
        entities: list[Any],
        entity_type: str,
        max_advice_per_entity: int = 5,
    ) -> list[CoachingReport]:
        """
        Generate coaching reports for multiple entities.

        Args:
            entities: List of Django model instances
            entity_type: Type of entities
            max_advice_per_entity: Maximum advice per entity

        Returns:
            List of CoachingReport objects
        """
        return [self.generate_for_entity(entity, entity_type, max_advice_per_entity) for entity in entities]

    def _convert_delta_advice(
        self,
        delta_advice: list[CoachingAdvice],
        delta: QualityDelta,
    ) -> list[UnifiedCoachingAdvice]:
        """Convert delta CoachingAdvice to UnifiedCoachingAdvice."""
        unified = []

        for advice in delta_advice:
            # Map category
            if advice.category == "missing_field":
                category = CoachingCategory.MISSING_FIELD
            elif advice.category == "constraint":
                category = CoachingCategory.CONSTRAINT_VIOLATION
            elif advice.category == "relationship":
                category = CoachingCategory.RELATIONSHIP_GAP
            else:
                category = CoachingCategory.ENHANCEMENT

            # Map urgency from priority
            if advice.priority <= 2:
                urgency = CoachingUrgency.CRITICAL
            elif advice.priority <= 4:
                urgency = CoachingUrgency.HIGH
            elif advice.priority <= 6:
                urgency = CoachingUrgency.MEDIUM
            else:
                urgency = CoachingUrgency.LOW

            unified.append(
                UnifiedCoachingAdvice(
                    id=self._next_advice_id(),
                    priority=advice.priority,
                    urgency=urgency,
                    category=category,
                    title=f"{category.value.replace('_', ' ').title()}: {advice.target_field or 'General'}",
                    description=advice.reason,
                    action=advice.suggestion,
                    target_entity=delta.entity_id,
                    target_field=advice.target_field,
                    auto_actionable=advice.auto_actionable,
                    confidence=advice.confidence,
                    sources=["quality_delta"],
                )
            )

        return unified

    def _generate_emergence_advice(
        self,
        entity: Any,
        entity_type: str,
    ) -> list[UnifiedCoachingAdvice]:
        """Generate advice from emergence patterns."""
        advice = []
        entity_id = f"{entity_type}_{entity.id}"

        # Check if entity is well-connected
        if self.emergence_edges:
            weight = compute_node_weight(entity_id, self.emergence_edges)

            if weight == 0.0:
                # Entity is isolated
                advice.append(
                    UnifiedCoachingAdvice(
                        id=self._next_advice_id(),
                        priority=5,
                        urgency=CoachingUrgency.MEDIUM,
                        category=CoachingCategory.KNOWLEDGE_GAP,
                        title="Isolated Knowledge",
                        description=f"This {entity_type} has no connections in the knowledge graph",
                        action=f"Link this {entity_type} to related concepts, tasks, or documents",
                        target_entity=str(entity.id),
                        auto_actionable=True,
                        confidence=0.8,
                        sources=["emergence"],
                    )
                )
            elif weight < 1.5:
                # Entity is weakly connected
                advice.append(
                    UnifiedCoachingAdvice(
                        id=self._next_advice_id(),
                        priority=7,
                        urgency=CoachingUrgency.LOW,
                        category=CoachingCategory.CONNECTION,
                        title="Weak Connections",
                        description=f"This {entity_type} has few connections (weight: {weight:.2f})",
                        action="Consider adding more links to related entities",
                        target_entity=str(entity.id),
                        auto_actionable=True,
                        confidence=0.6,
                        sources=["emergence"],
                    )
                )

        # Check relevant patterns
        for pattern in self.emergence_patterns:
            if entity_id in pattern.get("involved_nodes", []):
                if pattern.get("actionable") and pattern.get("confidence", 0) >= 0.7:
                    advice.append(
                        UnifiedCoachingAdvice(
                            id=self._next_advice_id(),
                            priority=6,
                            urgency=CoachingUrgency.MEDIUM,
                            category=CoachingCategory.PATTERN,
                            title=f"Pattern: {pattern.get('pattern_type', 'Unknown')}",
                            description=pattern.get("description", ""),
                            action=pattern.get("suggested_action", ""),
                            target_entity=str(entity.id),
                            auto_actionable=False,
                            confidence=pattern.get("confidence", 0.5),
                            sources=["emergence_pattern"],
                        )
                    )

        return advice

    def _generate_summary(
        self,
        delta: QualityDelta,
        advice: list[UnifiedCoachingAdvice],
    ) -> str:
        """Generate human-readable summary."""
        if delta.is_healthy and not advice:
            return f"This entity is in good health with a score of {delta.completeness_score}%."

        parts = []

        # Health assessment
        if delta.completeness_score >= 90:
            parts.append(f"Good health ({delta.completeness_score}%)")
        elif delta.completeness_score >= 70:
            parts.append(f"Moderate health ({delta.completeness_score}%)")
        else:
            parts.append(f"Needs attention ({delta.completeness_score}%)")

        # Critical count
        critical = [a for a in advice if a.urgency == CoachingUrgency.CRITICAL]
        if critical:
            parts.append(f"{len(critical)} critical issue(s)")

        # Top action
        if advice:
            parts.append(f"Top priority: {advice[0].action}")

        return ". ".join(parts) + "."


# =============================================================================
# CONVENIENCE FUNCTIONS
# =============================================================================


def generate_unified_coaching(
    entity: Any,
    entity_type: str,
    emergence_edges: list[KnowledgeEdgeLike] | None = None,
    emergence_patterns: list[dict[str, Any]] | None = None,
    max_advice: int = 10,
) -> CoachingReport:
    """
    Generate unified coaching for an entity.

    Convenience function that creates a CoachingEngine and generates advice.

    Args:
        entity: Django model instance or entity-like object
        entity_type: One of 'task', 'sprint', 'document'
        emergence_edges: Optional edges from v_emergence
        emergence_patterns: Optional patterns from v_emergence
        max_advice: Maximum number of advice items

    Returns:
        CoachingReport with unified advice
    """
    engine = CoachingEngine(
        emergence_edges=emergence_edges,
        emergence_patterns=emergence_patterns,
    )
    return engine.generate_for_entity(entity, entity_type, max_advice)


def generate_sprint_coaching(
    sprint: Any,
    include_tasks: bool = True,
    emergence_edges: list[KnowledgeEdgeLike] | None = None,
    emergence_patterns: list[dict[str, Any]] | None = None,
) -> dict[str, Any]:
    """
    Generate comprehensive coaching for a sprint and its tasks.

    Args:
        sprint: Django Sprint model instance
        include_tasks: Whether to include task-level coaching
        emergence_edges: Optional edges from v_emergence
        emergence_patterns: Optional patterns from v_emergence

    Returns:
        Dict with sprint report and optional task reports
    """
    engine = CoachingEngine(
        emergence_edges=emergence_edges,
        emergence_patterns=emergence_patterns,
    )

    # Generate sprint coaching
    sprint_report = engine.generate_for_entity(sprint, "sprint")

    result = {
        "sprint": sprint_report.to_dict(),
        "task_reports": [],
    }

    if include_tasks:
        tasks = list(sprint.tasks.all())
        task_reports = engine.generate_for_batch(tasks, "task", max_advice_per_entity=3)
        result["task_reports"] = [r.to_dict() for r in task_reports]

        # Aggregate metrics
        total_health = sum(r.health_score for r in task_reports)
        avg_health = total_health / len(task_reports) if task_reports else Decimal("0")

        result["aggregate"] = {
            "task_count": len(tasks),
            "average_task_health": float(avg_health),
            "total_critical_issues": sum(r.critical_count for r in task_reports),
            "total_advice_items": sum(r.advice_count for r in task_reports),
        }

    return result
