from __future__ import annotations

import asyncio
import logging
from typing import Any

from app.config import settings
from app.database import async_session
from app.agents.learning.bridge import update_learning_decision_with_bridge
from app.agents.learning.pattern_miner import LearningPatternMiner
from app.agents.learning.audit import build_learning_audit_entry
from app.agents.learning.retrospector import build_session_retrospective
from app.agents.learning.signal_extractor import RetrospectiveSignalExtractor
from app.agents.learning.skill_candidate_builder import SkillCandidateBuilder
from app.agents.learning.store import LearningArtifactStore, SessionRetrospectiveStore
from app.agents.schemas.learning import LearningDecision, SessionRetrospective
from app.agents.skills.evaluator import SkillPromotionEvaluator

logger = logging.getLogger(__name__)


def _enrich_retrospective(retrospective: SessionRetrospective) -> SessionRetrospective:
    signals = RetrospectiveSignalExtractor().extract(retrospective)
    patterns = LearningPatternMiner().mine(signals)
    skill_candidates = SkillCandidateBuilder().build(patterns)

    decision = LearningDecision(
        decision="create_candidate" if skill_candidates else ("reinforce_memory" if signals else "defer"),
        explanation=(
            "Retrospective produced reusable candidate skills."
            if skill_candidates
            else "Retrospective only reinforces memory-like evidence."
            if signals
            else "No stable signal was extracted from this retrospective."
        ),
        evidence_refs=(skill_candidates[0].evidence_refs if skill_candidates else retrospective.evidence_refs[:3]),
        metadata={
            "signal_count": len(signals),
            "pattern_count": len(patterns),
            "skill_candidate_count": len(skill_candidates),
        },
    )

    retrospective.learning_signals = signals
    retrospective.pattern_candidates = patterns
    retrospective.skill_candidates = skill_candidates
    retrospective.learning_decision = update_learning_decision_with_bridge(decision, signals)
    return retrospective


def _build_learning_artifacts(retrospective: SessionRetrospective) -> list[dict[str, object]]:
    artifacts: list[dict[str, object]] = []
    for signal in retrospective.learning_signals:
        artifacts.append(
            {
                "artifact_type": "signal",
                "artifact_key": signal.signal_type,
                "summary_text": signal.explanation or signal.signal_type,
                "payload": signal.model_dump(mode="json"),
            }
        )
    for pattern in retrospective.pattern_candidates:
        artifacts.append(
            {
                "artifact_type": "pattern_candidate",
                "artifact_key": pattern.pattern_type,
                "summary_text": pattern.description,
                "payload": pattern.model_dump(mode="json"),
            }
        )
    for candidate in retrospective.skill_candidates:
        artifacts.append(
            {
                "artifact_type": "skill_candidate",
                "artifact_key": candidate.name,
                "summary_text": candidate.summary,
                "payload": candidate.model_dump(mode="json"),
            }
        )
    if retrospective.learning_decision is not None:
        artifacts.append(
            {
                "artifact_type": "learning_decision",
                "artifact_key": retrospective.learning_decision.decision,
                "summary_text": retrospective.learning_decision.explanation,
                "payload": retrospective.learning_decision.model_dump(mode="json"),
            }
        )
        artifacts.append(
            {
                "artifact_type": "learning_audit",
                "artifact_key": retrospective.retrospective_id or "retrospective",
                "summary_text": retrospective.learning_decision.explanation,
                "payload": build_learning_audit_entry(retrospective),
            }
        )
    return artifacts


def _build_lifecycle_artifacts(decisions: list) -> list[dict[str, object]]:
    artifacts: list[dict[str, object]] = []
    for decision in decisions:
        artifacts.append(
            {
                "artifact_type": "skill_lifecycle_decision",
                "artifact_key": getattr(decision, "skill_name", None) or "skill",
                "summary_text": getattr(decision, "reason", ""),
                "payload": decision.model_dump(mode="json"),
            }
        )
    return artifacts


async def persist_retrospective(
    *,
    user_id: str,
    conversation_id: str,
    request_message_id: str | None,
    response_message_id: str | None,
    query_text: str,
    final_response: str | None,
    state: dict[str, Any] | None,
) -> None:
    retrospective = build_session_retrospective(
        request_id=response_message_id or request_message_id or conversation_id,
        session_id=conversation_id,
        user_query=query_text,
        state=state,
        runtime_context={"user_id": user_id},
    )
    retrospective = _enrich_retrospective(retrospective)

    async with async_session() as session:
        saved = await SessionRetrospectiveStore(session).save(retrospective)
        lifecycle_decisions = []
        if settings.ENABLE_SKILL_PROMOTION:
            lifecycle_decisions = await SkillPromotionEvaluator(session).sync_retrospective(
                user_id=user_id,
                retrospective=retrospective,
            )
        if settings.ENABLE_LEARNING_SIGNALS:
            await LearningArtifactStore(session).save_batch(
                user_id=user_id,
                conversation_id=conversation_id,
                retrospective_id=saved.id,
                artifacts=[
                    *_build_learning_artifacts(retrospective),
                    *_build_lifecycle_artifacts(lifecycle_decisions),
                ],
            )


def schedule_retrospective_job(**kwargs) -> asyncio.Task[None] | None:
    if not settings.ENABLE_RETROSPECTIVE:
        return None
    try:
        task = asyncio.create_task(persist_retrospective(**kwargs))
    except RuntimeError:
        return None

    def _handle_completion(done_task: asyncio.Task[None]) -> None:
        try:
            done_task.result()
        except Exception:
            logger.exception("retrospective_job_failed")

    task.add_done_callback(_handle_completion)
    return task


def schedule_retrospective_learning_event(
    *,
    user_id: str,
    conversation_id: str,
    retrospective: SessionRetrospective,
    session_factory=async_session,
) -> asyncio.Task[None] | None:
    if not settings.ENABLE_RETROSPECTIVE:
        return None

    async def _persist_existing() -> None:
        async with session_factory() as session:
            enriched = _enrich_retrospective(retrospective)
            saved = await SessionRetrospectiveStore(session).save(enriched)
            lifecycle_decisions = []
            if settings.ENABLE_SKILL_PROMOTION:
                lifecycle_decisions = await SkillPromotionEvaluator(session).sync_retrospective(
                    user_id=user_id,
                    retrospective=enriched,
                )
            if settings.ENABLE_LEARNING_SIGNALS:
                await LearningArtifactStore(session).save_batch(
                    user_id=user_id,
                    conversation_id=conversation_id,
                    retrospective_id=saved.id,
                    artifacts=[
                        *_build_learning_artifacts(enriched),
                        *_build_lifecycle_artifacts(lifecycle_decisions),
                    ],
                )

    try:
        task = asyncio.create_task(_persist_existing())
    except RuntimeError:
        return None

    def _handle_completion(done_task: asyncio.Task[None]) -> None:
        try:
            done_task.result()
        except Exception:
            logger.exception(
                "retrospective_learning_event_failed",
                extra={
                    "details": {
                        "user_id": user_id,
                        "conversation_id": conversation_id,
                    }
                },
            )

    task.add_done_callback(_handle_completion)
    return task
