"""Skill learner for extracting reusable skills from successful executions.

When the agent successfully completes a task, this module analyzes
the execution and extracts generalizable skills.
"""

from __future__ import annotations

from uuid import uuid4

from promethian.core.types import Skill, ExecutionResult, ExecutionStatus
from promethian.utils.claude import ClaudeClient


SKILL_EXTRACTION_SYSTEM = """You are a skill extraction assistant for a self-improving AI agent.

Your job is to analyze successful task executions and extract reusable skills.
A skill is procedural knowledge - a guide for how to approach similar problems.

Good skills:
1. Are generalizable (not too specific to one task)
2. Capture the methodology and approach
3. Include tips and pitfalls to avoid
4. Can help with similar future tasks

Output skills as markdown with clear structure:
- When to use this skill
- Step-by-step approach
- Key considerations
- Common pitfalls
- Success tips"""


class SkillLearner:
    """
    Extracts reusable skills from successful task executions.

    Analyzes what made an execution successful and generalizes
    it into procedural knowledge that can help with future tasks.
    """

    def __init__(self, claude: ClaudeClient):
        self.claude = claude

    async def should_extract_skill(self, result: ExecutionResult) -> bool:
        """
        Determine if a skill should be extracted from this execution.

        Criteria:
        1. Task succeeded
        2. Required multiple attempts with self-healing (learned something new)
        3. AND task involved genuine problem-solving, not just tool orchestration
        """
        if result.status != ExecutionStatus.SUCCESS:
            return False

        # Only extract if we had to self-heal (actually learned something)
        if result.total_attempts <= len(result.step_results):
            # No self-healing needed - likely just straightforward tool usage
            return False

        # Count tools used
        tools_used = set()
        for step_result in result.step_results:
            tools_used.update(step_result.tools_used)

        # Ask Claude if this is worth learning
        prompt = f"""Should we create a reusable skill from this task execution?

Task: {result.task}
Outcome: Success after self-healing
Steps: {len(result.step_results)}
Attempts: {result.total_attempts}
Tools used: {len(tools_used)}

A skill should ONLY be created if:
1. We discovered a non-obvious approach or workaround
2. The self-healing revealed something valuable to remember
3. This isn't just simple tool orchestration (using tools in sequence)
4. Future similar tasks would genuinely benefit from this knowledge

Do NOT create a skill if:
- We just combined existing tools in an obvious way
- The fix was a simple retry or minor parameter adjustment
- The approach is already well-documented elsewhere

Respond with JSON: {{"should_create": true/false, "reason": "brief explanation"}}"""

        try:
            response = await self.claude.complete_json(prompt=prompt)
            return response.get("should_create", False)
        except Exception:
            return False

    async def extract_skill(
        self,
        result: ExecutionResult,
        additional_context: str | None = None,
    ) -> Skill | None:
        """
        Extract a skill from a successful execution.

        Args:
            result: The successful execution result
            additional_context: Optional extra context

        Returns:
            A new Skill, or None if extraction fails
        """
        if result.status != ExecutionStatus.SUCCESS:
            return None

        # Build step descriptions
        steps_desc = []
        for i, step_result in enumerate(result.step_results):
            if i < len(result.plan.steps):
                step = result.plan.steps[i]
                steps_desc.append(f"- {step.description}")
                if step_result.healing_log:
                    steps_desc.append(f"  (required {len(step_result.healing_log)} healing attempts)")

        prompt = f"""Extract a reusable skill from this successful task execution.

Task: {result.task}

Steps Taken:
{chr(10).join(steps_desc)}

Total Attempts: {result.total_attempts}
Execution Time: {result.execution_time:.1f}s

{f"Additional Context: {additional_context}" if additional_context else ""}

Create a skill that captures:
1. When to use this approach (what kinds of tasks)
2. The key methodology and steps
3. Important considerations
4. Common pitfalls to avoid
5. Tips for success

Respond with JSON:
{{
    "name": "descriptive-skill-name",
    "description": "One sentence describing when to use this skill",
    "content": "# Skill Title\\n\\nFull markdown content with sections...",
    "tags": ["relevant", "tags", "max-5"]
}}"""

        try:
            data = await self.claude.complete_json(
                prompt=prompt,
                system=SKILL_EXTRACTION_SYSTEM,
            )

            return Skill(
                id=f"skill_{uuid4().hex[:12]}",
                name=data.get("name", "Unnamed Skill"),
                description=data.get("description", ""),
                content=data.get("content", ""),
                tags=data.get("tags", [])[:5],
                source_task=result.task[:200],
            )
        except Exception:
            return None

    async def improve_skill(
        self,
        skill: Skill,
        new_execution: ExecutionResult,
        what_changed: str | None = None,
    ) -> Skill | None:
        """
        Improve an existing skill based on a new execution.

        If we used a skill and learned something new, update it.

        Args:
            skill: The existing skill
            new_execution: The new execution that used this skill
            what_changed: Description of what was learned

        Returns:
            An improved Skill, or None if no improvement needed
        """
        if new_execution.status != ExecutionStatus.SUCCESS:
            return None

        prompt = f"""Make MINIMAL improvements to this skill based on what was learned during execution.

IMPORTANT: Be extremely conservative. The existing skill has been proven to work.
Only make the smallest possible changes needed to address the specific issue encountered.
Do NOT rewrite or restructure the skill. Do NOT add unnecessary content.

Existing Skill:
Name: {skill.name}
Description: {skill.description}
Content:
{skill.content}

What happened during execution:
Task: {new_execution.task}
Attempts needed: {new_execution.total_attempts}
{f"Issue encountered and fix: {what_changed}" if what_changed else ""}

Rules for improvement:
1. PREFER returning no_change if the issue was minor or one-off
2. Only add a brief note about the pitfall encountered - don't restructure
3. Keep 95%+ of the original content unchanged
4. Add at most 1-2 sentences addressing the specific issue
5. Do NOT change the name, description, or tags unless absolutely necessary

If the issue doesn't warrant a permanent change, respond with:
{{"no_change": true, "reason": "Issue was minor/one-off, not worth permanent change"}}

Otherwise respond with the minimally improved skill JSON:
{{
    "name": "skill-name",
    "description": "...",
    "content": "...",
    "tags": [...]
}}"""

        try:
            data = await self.claude.complete_json(
                prompt=prompt,
                system=SKILL_EXTRACTION_SYSTEM,
            )

            if data.get("no_change"):
                return None

            return Skill(
                id=f"skill_{uuid4().hex[:12]}",
                name=data.get("name", skill.name),
                description=data.get("description", skill.description),
                content=data.get("content", skill.content),
                tags=data.get("tags", skill.tags)[:5],
                derived_from=skill.id,
                source_task=new_execution.task[:200],
                version=skill.version + 1,
            )
        except Exception:
            return None

    async def merge_skills(
        self,
        skills: list[Skill],
        reason: str | None = None,
    ) -> Skill | None:
        """
        Merge multiple related skills into one comprehensive skill.

        Useful when several skills cover overlapping territory.

        Args:
            skills: Skills to merge
            reason: Why these should be merged

        Returns:
            A merged Skill, or None if merge fails
        """
        if len(skills) < 2:
            return None

        skills_desc = []
        for skill in skills:
            skills_desc.append(f"""
### {skill.name}
{skill.description}

{skill.content}
""")

        prompt = f"""Merge these related skills into one comprehensive skill.

Skills to merge:
{'---'.join(skills_desc)}

{f"Reason for merge: {reason}" if reason else ""}

Create a single skill that:
1. Combines all the knowledge from the input skills
2. Removes redundancy
3. Organizes information clearly
4. Is more comprehensive than any single input

Respond with the merged skill JSON:
{{
    "name": "merged-skill-name",
    "description": "...",
    "content": "...",
    "tags": [...]
}}"""

        try:
            data = await self.claude.complete_json(
                prompt=prompt,
                system=SKILL_EXTRACTION_SYSTEM,
            )

            return Skill(
                id=f"skill_{uuid4().hex[:12]}",
                name=data.get("name", "Merged Skill"),
                description=data.get("description", ""),
                content=data.get("content", ""),
                tags=data.get("tags", [])[:5],
                # Track which skills this came from
                depends_on=[s.id for s in skills],
            )
        except Exception:
            return None
