from __future__ import annotations

import json
from datetime import datetime, timedelta, timezone

from pydantic import BaseModel, Field

from nexus.persona.reflector import ReflectionRecord
from nexus.protocols.providers.base import AbstractProvider, LLMRequest, Message


class EvolutionEntry(BaseModel):
    """Prompt guideline generated from reflection patterns."""

    guideline: str
    confidence: float
    source_reflections: int
    created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
    expires_at: datetime | None = None


class PromptEvolver:
    """Evolves system prompts from accumulated reflections."""

    def __init__(self, provider: AbstractProvider, evolution_trigger_n: int = 5, guideline_ttl_hours: int = 24) -> None:
        self.provider = provider
        self.evolution_trigger_n = evolution_trigger_n
        self.guideline_ttl_hours = guideline_ttl_hours

    async def evolve(self, agent_id: str, base_system_prompt: str, reflections: list[ReflectionRecord]) -> str:
        """Generate and append concise learned guidelines to base prompt."""
        recent = reflections[-self.evolution_trigger_n :]
        if not recent:
            return base_system_prompt
        patterns = self._extract_patterns(recent)
        prompt = self._build_evolution_prompt(base_system_prompt, patterns)
        req = LLMRequest(messages=[Message(role="user", content=prompt)], temperature=0.2, max_tokens=500)
        resp = await self.provider.complete(req)

        guidelines = _parse_guidelines(resp.content)
        if not guidelines:
            return base_system_prompt

        formatted = "\n".join(f"{i}. {line}" for i, line in enumerate(guidelines[:5], start=1))
        return base_system_prompt + "\n\n# Learned Guidelines:\n" + formatted

    def _extract_patterns(self, reflections: list[ReflectionRecord]) -> str:
        """Aggregate failure patterns and suggestions."""
        lines: list[str] = []
        for r in reflections:
            for poor in r.what_went_poorly:
                lines.append(f"Issue: {poor}")
            for sug in r.suggestions:
                lines.append(f"Suggestion: {sug}")
        return "\n".join(lines)

    def _build_evolution_prompt(self, base_prompt: str, patterns: str) -> str:
        """Build JSON-list output instruction prompt."""
        return (
            "Given this base system prompt and observed failure patterns, output 3-5 concise guidelines "
            "to append to the prompt. Return JSON list of strings only.\n\n"
            f"Base prompt:\n{base_prompt}\n\nPatterns:\n{patterns}"
        )


def _parse_guidelines(text: str) -> list[str]:
    import re

    block = re.search(r"```json\s*(.*?)\s*```", text, flags=re.DOTALL)
    raw = block.group(1) if block else text
    try:
        parsed = json.loads(raw)
        if isinstance(parsed, list):
            return [str(x) for x in parsed if str(x).strip()]
    except ValueError:
        pass
    return [line.strip(" -") for line in text.splitlines() if line.strip()][:5]
