"""
Agent Onboarding & Training — Get new agents up to speed fast.

When a new agent joins the network, this module handles the full onboarding
lifecycle: welcome flow, knowledge transfer, skill verification, mentorship
pairing, and progress tracking.

Storage:
  state/broker/onboarding/
    knowledge/{topic}.json       — knowledge base articles
    checklists/{session_id}.json — per-agent onboarding progress
    mentors.json                 — mentor assignments
"""

import json
import uuid
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Optional

from brynq.server import (
    BROKER_DIR,
    CHANNELS_DIR,
    SESSION_ID,
    SESSION_NAME,
    PLATFORM,
    _append_jsonl_sync,
    _write_json_sync,
    _read_jsonl_sync,
)

ONBOARDING_DIR = BROKER_DIR / "onboarding"
KNOWLEDGE_DIR = ONBOARDING_DIR / "knowledge"
CHECKLISTS_DIR = ONBOARDING_DIR / "checklists"
MENTORS_FILE = ONBOARDING_DIR / "mentors.json"

for _d in (KNOWLEDGE_DIR, CHECKLISTS_DIR):
    _d.mkdir(parents=True, exist_ok=True)

# Default onboarding steps every new agent should complete.
DEFAULT_STEPS = [
    "welcome_received",
    "knowledge_base_reviewed",
    "skills_verified",
    "mentor_assigned",
    "first_message_sent",
    "project_briefing_read",
]


# ── Knowledge Base ────────────────────────────────────────────────────────


def add_knowledge(
    topic: str,
    content: str,
    tags: Optional[list[str]] = None,
) -> dict:
    """Add or update a knowledge base article for onboarding.

    Articles are stored as individual JSON files keyed by topic slug.
    New agents review these during onboarding to understand project
    context, conventions, and tribal knowledge.
    """
    slug = _slugify(topic)
    path = KNOWLEDGE_DIR / f"{slug}.json"

    existing = _load_json(path)
    now = datetime.now(timezone.utc).isoformat()

    article = {
        "topic": topic,
        "slug": slug,
        "content": content,
        "tags": tags or existing.get("tags", []),
        "created_by": SESSION_ID,
        "created_by_name": SESSION_NAME,
        "created": existing.get("created", now),
        "updated": now,
        "version": existing.get("version", 0) + 1,
    }

    _write_json_sync(path, article)

    # Announce new knowledge
    entry = {
        "id": uuid.uuid4().hex[:12],
        "timestamp": now,
        "session_id": SESSION_ID,
        "session_name": SESSION_NAME,
        "platform": PLATFORM,
        "channel": "_system",
        "msg_type": "status",
        "message": f"{SESSION_NAME}@{PLATFORM} added knowledge article: {topic}",
    }
    _append_jsonl_sync(CHANNELS_DIR / "_system.jsonl", entry)

    return article


def get_knowledge(topic: str) -> Optional[dict]:
    """Retrieve a knowledge base article by topic."""
    slug = _slugify(topic)
    path = KNOWLEDGE_DIR / f"{slug}.json"
    data = _load_json(path)
    return data if data else None


def list_knowledge(tag: Optional[str] = None) -> list[dict]:
    """List all knowledge base articles, optionally filtered by tag.

    Returns summaries (topic, tags, updated) without full content
    to keep the listing compact.
    """
    articles = []
    for f in sorted(KNOWLEDGE_DIR.glob("*.json")):
        try:
            data = json.loads(f.read_text("utf-8"))
        except (json.JSONDecodeError, OSError):
            continue

        if tag and tag.lower() not in [t.lower() for t in data.get("tags", [])]:
            continue

        articles.append({
            "topic": data.get("topic", f.stem),
            "slug": data.get("slug", f.stem),
            "tags": data.get("tags", []),
            "created_by_name": data.get("created_by_name", "unknown"),
            "updated": data.get("updated", ""),
            "version": data.get("version", 1),
        })

    return articles


# ── Onboarding Lifecycle ──────────────────────────────────────────────────


def start_onboarding(
    session_id: Optional[str] = None,
    session_name: Optional[str] = None,
    platform: Optional[str] = None,
    extra_steps: Optional[list[str]] = None,
) -> dict:
    """Begin the onboarding flow for a new agent.

    Creates a checklist with default steps (plus any extras), sends a
    welcome message to #_system, and returns the initial onboarding state.
    """
    sid = session_id or SESSION_ID
    sname = session_name or SESSION_NAME
    plat = platform or PLATFORM
    now = datetime.now(timezone.utc).isoformat()

    steps = list(DEFAULT_STEPS)
    for s in (extra_steps or []):
        if s not in steps:
            steps.append(s)

    checklist = {
        "session_id": sid,
        "session_name": sname,
        "platform": plat,
        "started": now,
        "completed": None,
        "steps": {step: {"done": False, "completed_at": None} for step in steps},
        "mentor_id": None,
        "mentor_name": None,
        "briefings_read": [],
    }

    path = CHECKLISTS_DIR / f"{sid}.json"
    _write_json_sync(path, checklist)

    # Mark the welcome step as done immediately
    checklist["steps"]["welcome_received"] = {
        "done": True,
        "completed_at": now,
    }
    _write_json_sync(path, checklist)

    # Send welcome message
    knowledge_count = len(list(KNOWLEDGE_DIR.glob("*.json")))
    welcome_msg = (
        f"Welcome {sname}@{plat}! Onboarding started. "
        f"{knowledge_count} knowledge article(s) available. "
        f"Steps to complete: {', '.join(steps)}"
    )
    entry = {
        "id": uuid.uuid4().hex[:12],
        "timestamp": now,
        "session_id": SESSION_ID,
        "session_name": "brynq-onboarding",
        "platform": "brynq",
        "channel": "_system",
        "msg_type": "status",
        "message": welcome_msg,
    }
    _append_jsonl_sync(CHANNELS_DIR / "_system.jsonl", entry)

    return checklist


def complete_step(
    session_id: Optional[str] = None,
    step: str = "",
) -> dict:
    """Mark an onboarding step as complete for an agent.

    Returns the updated checklist. If all steps are done, marks the
    entire onboarding as completed.
    """
    sid = session_id or SESSION_ID
    path = CHECKLISTS_DIR / f"{sid}.json"
    checklist = _load_json(path)

    if not checklist:
        return {"error": f"No onboarding session found for {sid}"}

    if step not in checklist.get("steps", {}):
        return {"error": f"Unknown step '{step}'. Valid: {list(checklist['steps'].keys())}"}

    now = datetime.now(timezone.utc).isoformat()
    checklist["steps"][step] = {"done": True, "completed_at": now}

    # Check if all steps are now complete
    all_done = all(s["done"] for s in checklist["steps"].values())
    if all_done and not checklist.get("completed"):
        checklist["completed"] = now
        # Announce graduation
        sname = checklist.get("session_name", sid)
        plat = checklist.get("platform", "unknown")
        entry = {
            "id": uuid.uuid4().hex[:12],
            "timestamp": now,
            "session_id": SESSION_ID,
            "session_name": "brynq-onboarding",
            "platform": "brynq",
            "channel": "_system",
            "msg_type": "status",
            "message": f"{sname}@{plat} completed onboarding! All steps done.",
        }
        _append_jsonl_sync(CHANNELS_DIR / "_system.jsonl", entry)

    _write_json_sync(path, checklist)
    return checklist


def get_onboarding_status(session_id: Optional[str] = None) -> dict:
    """Check an agent's onboarding progress.

    Returns the full checklist with a summary of completed/remaining steps.
    """
    sid = session_id or SESSION_ID
    path = CHECKLISTS_DIR / f"{sid}.json"
    checklist = _load_json(path)

    if not checklist:
        return {"error": f"No onboarding session found for {sid}", "session_id": sid}

    steps = checklist.get("steps", {})
    completed = [name for name, info in steps.items() if info.get("done")]
    remaining = [name for name, info in steps.items() if not info.get("done")]

    return {
        **checklist,
        "summary": {
            "total_steps": len(steps),
            "completed_count": len(completed),
            "remaining_count": len(remaining),
            "completed_steps": completed,
            "remaining_steps": remaining,
            "progress_pct": round(len(completed) / max(len(steps), 1) * 100, 1),
            "is_complete": checklist.get("completed") is not None,
        },
    }


# ── Mentorship ────────────────────────────────────────────────────────────


def assign_mentor(
    mentee_id: str,
    mentor_id: str,
    mentor_name: Optional[str] = None,
    mentee_name: Optional[str] = None,
) -> dict:
    """Pair a new agent with an experienced mentor.

    Updates the mentee's checklist, records the assignment in mentors.json,
    and announces the pairing on #_system.
    """
    now = datetime.now(timezone.utc).isoformat()

    # Load or create the mentors registry
    mentors = _load_json(MENTORS_FILE)
    if not mentors:
        mentors = {"assignments": []}

    # Resolve names from checklists/profiles if not provided
    m_name = mentor_name or _resolve_agent_name(mentor_id)
    me_name = mentee_name or _resolve_agent_name(mentee_id)

    assignment = {
        "mentee_id": mentee_id,
        "mentee_name": me_name,
        "mentor_id": mentor_id,
        "mentor_name": m_name,
        "assigned_at": now,
        "assigned_by": SESSION_ID,
    }

    # Replace existing assignment for this mentee, or append
    mentors["assignments"] = [
        a for a in mentors.get("assignments", [])
        if a.get("mentee_id") != mentee_id
    ]
    mentors["assignments"].append(assignment)
    _write_json_sync(MENTORS_FILE, mentors)

    # Update the mentee's checklist if it exists
    checklist_path = CHECKLISTS_DIR / f"{mentee_id}.json"
    checklist = _load_json(checklist_path)
    if checklist:
        checklist["mentor_id"] = mentor_id
        checklist["mentor_name"] = m_name
        if "mentor_assigned" in checklist.get("steps", {}):
            checklist["steps"]["mentor_assigned"] = {
                "done": True,
                "completed_at": now,
            }
        _write_json_sync(checklist_path, checklist)

    # Announce
    entry = {
        "id": uuid.uuid4().hex[:12],
        "timestamp": now,
        "session_id": SESSION_ID,
        "session_name": "brynq-onboarding",
        "platform": "brynq",
        "channel": "_system",
        "msg_type": "status",
        "message": f"Mentor assigned: {m_name} -> {me_name}",
    }
    _append_jsonl_sync(CHANNELS_DIR / "_system.jsonl", entry)

    return assignment


def get_mentor(mentee_id: str) -> Optional[dict]:
    """Look up the current mentor assignment for an agent."""
    mentors = _load_json(MENTORS_FILE)
    if not mentors:
        return None
    for a in mentors.get("assignments", []):
        if a.get("mentee_id") == mentee_id:
            return a
    return None


def list_mentorships() -> list[dict]:
    """List all active mentor-mentee pairings."""
    mentors = _load_json(MENTORS_FILE)
    if not mentors:
        return []
    return mentors.get("assignments", [])


# ── Skill Verification ───────────────────────────────────────────────────


def verify_skills(
    session_id: Optional[str] = None,
    claimed_skills: Optional[list[str]] = None,
) -> dict:
    """Verify whether an agent has the skills it claims.

    Cross-references claimed skills against the agent's profile
    (if one exists) and capability registrations. Returns a verification
    report indicating confirmed, unconfirmed, and registered-only skills.
    """
    sid = session_id or SESSION_ID
    claimed = [s.lower() for s in (claimed_skills or [])]

    # Check profile skills
    profile_skills: list[str] = []
    profile_path = BROKER_DIR / "profiles" / f"{sid}.json"
    profile = _load_json(profile_path)
    if profile:
        profile_skills = [s.lower() for s in profile.get("skills", [])]

    # Check registered capabilities
    cap_skills: list[str] = []
    cap_dir = BROKER_DIR / "capabilities"
    if cap_dir.exists():
        for f in cap_dir.glob(f"{sid}_*.json"):
            try:
                cap = json.loads(f.read_text("utf-8"))
                cap_skills.append(cap.get("capability", "").lower())
            except (json.JSONDecodeError, OSError):
                continue

    # Build verification report
    all_registered = set(profile_skills) | set(cap_skills)
    confirmed = [s for s in claimed if s in all_registered]
    unconfirmed = [s for s in claimed if s not in all_registered]
    extra = [s for s in all_registered if s not in claimed]

    now = datetime.now(timezone.utc).isoformat()
    report = {
        "session_id": sid,
        "verified_at": now,
        "claimed_skills": claimed,
        "confirmed": confirmed,
        "unconfirmed": unconfirmed,
        "registered_only": list(extra),
        "verification_rate": round(
            len(confirmed) / max(len(claimed), 1) * 100, 1
        ),
    }

    # Auto-complete the skills_verified step if checklist exists
    checklist_path = CHECKLISTS_DIR / f"{sid}.json"
    checklist = _load_json(checklist_path)
    if checklist and "skills_verified" in checklist.get("steps", {}):
        checklist["steps"]["skills_verified"] = {
            "done": True,
            "completed_at": now,
        }
        _write_json_sync(checklist_path, checklist)

    return report


# ── Project Briefings ─────────────────────────────────────────────────────


def create_briefing(
    project_name: str,
    summary: str,
    key_files: Optional[list[str]] = None,
    conventions: Optional[list[str]] = None,
) -> dict:
    """Create a project briefing that new agents can review.

    Briefings are stored as knowledge articles with the 'briefing' tag,
    providing structured project context: what the project does, which
    files matter, and what conventions to follow.
    """
    now = datetime.now(timezone.utc).isoformat()

    # Build structured content
    sections = [f"# {project_name}\n", summary, ""]

    if key_files:
        sections.append("## Key Files")
        for f in key_files:
            sections.append(f"- {f}")
        sections.append("")

    if conventions:
        sections.append("## Conventions")
        for c in conventions:
            sections.append(f"- {c}")
        sections.append("")

    content = "\n".join(sections)

    # Store as a knowledge article with 'briefing' tag
    tags = ["briefing", "project"]
    article = add_knowledge(
        topic=f"briefing-{_slugify(project_name)}",
        content=content,
        tags=tags,
    )

    # Also store structured metadata alongside the content
    article["project_name"] = project_name
    article["key_files"] = key_files or []
    article["conventions"] = conventions or []

    slug = _slugify(f"briefing-{_slugify(project_name)}")
    path = KNOWLEDGE_DIR / f"{slug}.json"
    _write_json_sync(path, article)

    return article


def get_briefing(project_name: str) -> Optional[dict]:
    """Retrieve a project briefing by project name."""
    return get_knowledge(f"briefing-{_slugify(project_name)}")


def list_briefings() -> list[dict]:
    """List all available project briefings."""
    return list_knowledge(tag="briefing")


# ── Welcome Flow (Auto-Detection) ────────────────────────────────────────


def detect_new_agent(
    session_id: Optional[str] = None,
    session_name: Optional[str] = None,
    platform: Optional[str] = None,
    auto_start: bool = True,
) -> dict:
    """Auto-detect whether an agent is new and needs onboarding.

    Checks if a checklist already exists for the session. If not,
    optionally starts onboarding automatically. Returns the agent's
    onboarding state either way.
    """
    sid = session_id or SESSION_ID
    sname = session_name or SESSION_NAME
    plat = platform or PLATFORM

    checklist_path = CHECKLISTS_DIR / f"{sid}.json"
    existing = _load_json(checklist_path)

    if existing:
        return {
            "is_new": False,
            "status": "already_onboarding" if not existing.get("completed") else "completed",
            "checklist": get_onboarding_status(sid),
        }

    if auto_start:
        checklist = start_onboarding(
            session_id=sid,
            session_name=sname,
            platform=plat,
        )
        return {
            "is_new": True,
            "status": "onboarding_started",
            "checklist": checklist,
        }

    return {
        "is_new": True,
        "status": "not_started",
        "session_id": sid,
    }


# ── Helpers ───────────────────────────────────────────────────────────────


def _slugify(text: str) -> str:
    """Convert text to a filesystem-safe slug."""
    slug = text.lower().strip()
    safe = []
    for ch in slug:
        if ch.isalnum() or ch in ("-", "_"):
            safe.append(ch)
        elif ch in (" ", "/", "\\", "."):
            safe.append("-")
    # Collapse consecutive dashes
    result = "-".join(part for part in "".join(safe).split("-") if part)
    return result or "untitled"


def _load_json(path: Path) -> dict:
    """Load a JSON file, returning empty dict on any error."""
    try:
        return json.loads(path.read_text("utf-8"))
    except (json.JSONDecodeError, FileNotFoundError, OSError):
        return {}


def _resolve_agent_name(session_id: str) -> str:
    """Try to resolve a human-readable name for a session ID.

    Checks the onboarding checklist first, then profiles, then falls
    back to the raw session ID.
    """
    checklist = _load_json(CHECKLISTS_DIR / f"{session_id}.json")
    if checklist.get("session_name"):
        return checklist["session_name"]

    profile = _load_json(BROKER_DIR / "profiles" / f"{session_id}.json")
    if profile.get("session_name"):
        return profile["session_name"]

    return session_id
